summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/Kconfig127
-rw-r--r--drivers/soc/qcom/Makefile3
-rw-r--r--drivers/soc/qcom/cmd-db.c2
-rw-r--r--drivers/soc/qcom/ice.c70
-rw-r--r--drivers/soc/qcom/llcc-qcom.c511
-rw-r--r--drivers/soc/qcom/mdt_loader.c27
-rw-r--r--drivers/soc/qcom/pmic_glink_altmode.c3
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c299
-rw-r--r--drivers/soc/qcom/qcom_aoss.c2
-rw-r--r--drivers/soc/qcom/qcom_pd_mapper.c47
-rw-r--r--drivers/soc/qcom/qcom_stats.c11
-rw-r--r--drivers/soc/qcom/rpmh-rsc.c50
-rw-r--r--drivers/soc/qcom/rpmh.c47
-rw-r--r--drivers/soc/qcom/smem.c133
-rw-r--r--drivers/soc/qcom/smem.h12
-rw-r--r--drivers/soc/qcom/smem_dramc.c430
-rw-r--r--drivers/soc/qcom/socinfo.c24
-rw-r--r--drivers/soc/qcom/ubwc_config.c342
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c11
19 files changed, 1684 insertions, 467 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 2caadbbcf830..2b524154d9fb 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -2,14 +2,25 @@
#
# QCOM Soc drivers
#
-menu "Qualcomm SoC drivers"
+menuconfig QCOM_SOC
+ bool "Qualcomm SoC drivers"
+ depends on ARCH_QCOM || COMPILE_TEST
+ default ARCH_QCOM
+ help
+ This collection of drivers is specific to Qualcomm System-on-Chips
+ and most of them are necessary for a fully functional boot of the
+ Linux kernel (plus a few debugging drivers).
+ Drivers can be skipped when building Linux kernel not intended to run
+ said processors.
+
+if QCOM_SOC
config QCOM_AOSS_QMP
tristate "Qualcomm AOSS Driver"
- depends on ARCH_QCOM || COMPILE_TEST
depends on MAILBOX
depends on COMMON_CLK && PM
select PM_GENERIC_DOMAINS
+ default ARCH_QCOM
help
This driver provides the means of communicating with and controlling
the low-power state for resources related to the remoteproc
@@ -18,8 +29,8 @@ config QCOM_AOSS_QMP
config QCOM_COMMAND_DB
tristate "Qualcomm Command DB"
- depends on ARCH_QCOM || COMPILE_TEST
depends on OF_RESERVED_MEM
+ default ARCH_QCOM
help
Command DB queries shared memory by key string for shared system
resources. Platform drivers that require to set state of a shared
@@ -27,8 +38,9 @@ config QCOM_COMMAND_DB
SoC specific identifier and information for the shared resources.
config QCOM_GENI_SE
- tristate "QCOM GENI Serial Engine Driver"
- depends on ARCH_QCOM || COMPILE_TEST
+ tristate "Qualcomm GENI Serial Engine Driver"
+ depends on ARM64 || COMPILE_TEST
+ default ARCH_QCOM
help
This driver is used to manage Generic Interface (GENI) firmware based
Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
@@ -36,8 +48,9 @@ config QCOM_GENI_SE
Engines present in the QUP.
config QCOM_GSBI
- tristate "QCOM General Serial Bus Interface"
- depends on ARCH_QCOM || COMPILE_TEST
+ tristate "Qualcomm General Serial Bus Interface"
+ depends on ARM || COMPILE_TEST
+ default ARCH_QCOM
select MFD_SYSCON
help
Say y here to enable GSBI support. The GSBI provides control
@@ -45,27 +58,19 @@ config QCOM_GSBI
devices to the output pins.
config QCOM_LLCC
- tristate "Qualcomm Technologies, Inc. LLCC driver"
- depends on ARCH_QCOM || COMPILE_TEST
+ tristate "Qualcomm LLCC driver"
select REGMAP_MMIO
+ default m if ARCH_QCOM
help
Qualcomm Technologies, Inc. platform specific
Last Level Cache Controller(LLCC) driver for platforms such as,
SDM845. This provides interfaces to clients that use the LLCC.
Say yes here to enable LLCC slice driver.
-config QCOM_KRYO_L2_ACCESSORS
- bool
- depends on (ARCH_QCOM || COMPILE_TEST) && ARM64
-
-config QCOM_MDT_LOADER
- tristate
- select QCOM_SCM
-
config QCOM_OCMEM
tristate "Qualcomm On Chip Memory (OCMEM) driver"
- depends on ARCH_QCOM
select QCOM_SCM
+ default m if ARCH_QCOM
help
The On Chip Memory (OCMEM) allocator allows various clients to
allocate memory from OCMEM based on performance, latency and power
@@ -77,7 +82,7 @@ config QCOM_PD_MAPPER
select QCOM_QMI_HELPERS
select QCOM_PDR_MSG
select AUXILIARY_BUS
- depends on NET && QRTR && (ARCH_QCOM || COMPILE_TEST)
+ depends on NET && QRTR
default QCOM_RPROC_COMMON
help
The Protection Domain Mapper maps registered services to the domains
@@ -85,15 +90,6 @@ config QCOM_PD_MAPPER
implementation of the service. It is a simpler alternative to the
userspace daemon.
-config QCOM_PDR_HELPERS
- tristate
- select QCOM_QMI_HELPERS
- select QCOM_PDR_MSG
- depends on NET
-
-config QCOM_PDR_MSG
- tristate
-
config QCOM_PMIC_PDCHARGER_ULOG
tristate "Qualcomm PMIC PDCharger ULOG driver"
depends on RPMSG
@@ -113,9 +109,11 @@ config QCOM_PMIC_GLINK
depends on DRM
depends on NET
depends on OF
+ depends on ARM64 || COMPILE_TEST
select AUXILIARY_BUS
select QCOM_PDR_HELPERS
select DRM_AUX_HPD_BRIDGE
+ default m if ARCH_QCOM
help
The Qualcomm PMIC GLINK driver provides access, over GLINK, to the
USB and battery firmware running on one of the coprocessors in
@@ -124,13 +122,8 @@ config QCOM_PMIC_GLINK
Say yes here to support USB-C and battery status on modern Qualcomm
platforms.
-config QCOM_QMI_HELPERS
- tristate
- depends on NET
-
config QCOM_RAMP_CTRL
tristate "Qualcomm Ramp Controller driver"
- depends on ARCH_QCOM || COMPILE_TEST
help
The Ramp Controller is used to program the sequence ID for pulse
swallowing, enable sequence and link sequence IDs for the CPU
@@ -139,8 +132,8 @@ config QCOM_RAMP_CTRL
config QCOM_RMTFS_MEM
tristate "Qualcomm Remote Filesystem memory driver"
- depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
+ default m if ARCH_QCOM
help
The Qualcomm remote filesystem memory driver is used for allocating
and exposing regions of shared memory with remote processors for the
@@ -151,7 +144,6 @@ config QCOM_RMTFS_MEM
config QCOM_RPM_MASTER_STATS
tristate "Qualcomm RPM Master stats"
- depends on ARCH_QCOM || COMPILE_TEST
help
The RPM Master sleep stats driver provides detailed per-subsystem
sleep/wake data, read from the RPM message RAM. It can be used to
@@ -162,8 +154,8 @@ config QCOM_RPM_MASTER_STATS
config QCOM_RPMH
tristate "Qualcomm RPM-Hardened (RPMH) Communication"
- depends on ARCH_QCOM || COMPILE_TEST
depends on (QCOM_COMMAND_DB || !QCOM_COMMAND_DB)
+ default ARCH_QCOM
help
Support for communication with the hardened-RPM blocks in
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
@@ -173,8 +165,8 @@ config QCOM_RPMH
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
- depends on ARCH_QCOM || COMPILE_TEST
depends on HWSPINLOCK
+ default ARCH_QCOM
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
The driver provides an interface to items in a heap shared among all
@@ -182,9 +174,9 @@ config QCOM_SMEM
config QCOM_SMD_RPM
tristate "Qualcomm Resource Power Manager (RPM) over SMD"
- depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n
+ default ARCH_QCOM
help
If you say yes to this option, support will be included for the
Resource Power Manager system found in the Qualcomm 8974 based
@@ -205,6 +197,7 @@ config QCOM_SMP2P
depends on QCOM_SMEM
select QCOM_SMEM_STATE
select IRQ_DOMAIN
+ default ARCH_QCOM
help
Say yes here to support the Qualcomm Shared Memory Point to Point
protocol.
@@ -215,6 +208,7 @@ config QCOM_SMSM
depends on QCOM_SMEM
select QCOM_SMEM_STATE
select IRQ_DOMAIN
+ default ARCH_QCOM
help
Say yes here to support the Qualcomm Shared Memory State Machine.
The state machine is represented by bits in shared memory.
@@ -223,24 +217,26 @@ config QCOM_SOCINFO
tristate "Qualcomm socinfo driver"
depends on QCOM_SMEM
select SOC_BUS
+ default m if ARCH_QCOM
help
Say yes here to support the Qualcomm socinfo driver, providing
information about the SoC to user space.
config QCOM_SPM
tristate "Qualcomm Subsystem Power Manager (SPM)"
- depends on ARCH_QCOM || COMPILE_TEST
- select QCOM_SCM
+ depends on QCOM_SCM
+ default ARCH_QCOM if ARM
help
Enable the support for the Qualcomm Subsystem Power Manager, used
to manage cores, L2 low power modes and to configure the internal
Adaptive Voltage Scaler parameters, where supported.
config QCOM_STATS
- tristate "Qualcomm Technologies, Inc. (QTI) Sleep stats driver"
- depends on (ARCH_QCOM && DEBUG_FS) || COMPILE_TEST
+ tristate "Qualcomm Sleep stats driver"
+ depends on DEBUG_FS || COMPILE_TEST
depends on QCOM_SMEM
depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
+ default m if ARCH_QCOM
help
Qualcomm Technologies, Inc. (QTI) Sleep stats driver to read
the shared memory exported by the remote processor related to
@@ -249,18 +245,18 @@ config QCOM_STATS
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
- depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
+ default m if ARCH_QCOM
help
Client driver for the WCNSS_CTRL SMD channel, used to download nv
firmware to a newly booted WCNSS chip.
config QCOM_APR
tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)"
- depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
depends on NET
select QCOM_PDR_HELPERS
+ default m if ARCH_QCOM
help
Enable APR IPC protocol support between
application processor and QDSP6. APR is
@@ -268,10 +264,11 @@ config QCOM_APR
ASM, ADM and AFE modules.
config QCOM_ICC_BWMON
- tristate "QCOM Interconnect Bandwidth Monitor driver"
- depends on ARCH_QCOM || COMPILE_TEST
+ tristate "Qualcomm Interconnect Bandwidth Monitor driver"
+ depends on ARM64 || COMPILE_TEST
select PM_OPP
select REGMAP_MMIO
+ default m if ARCH_QCOM
help
Sets up driver monitoring bandwidth on various interconnects and
based on that voting for interconnect bandwidth, adjusting their
@@ -282,23 +279,49 @@ config QCOM_ICC_BWMON
the fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high
memory throughput even with lower CPU frequencies.
-config QCOM_INLINE_CRYPTO_ENGINE
- tristate
- select QCOM_SCM
-
config QCOM_PBS
- tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS"
+ tristate "PBS trigger support for Qualcomm PMICs"
depends on SPMI
+ default m if ARCH_QCOM
help
This driver supports configuring software programmable boot sequencer (PBS)
trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs.
This module provides the APIs to the client drivers that wants to send the
PBS trigger event to the PBS RAM.
-endmenu
+endif
+
+# Options selected by other drivers from different subsystems must be outside
+# of the menuconfig if-block:
+
+config QCOM_INLINE_CRYPTO_ENGINE
+ tristate
+ select QCOM_SCM
+
+config QCOM_KRYO_L2_ACCESSORS
+ bool
+ depends on ARM64
+
+config QCOM_MDT_LOADER
+ tristate
+ select QCOM_SCM
+
+config QCOM_PDR_HELPERS
+ tristate
+ select QCOM_QMI_HELPERS
+ select QCOM_PDR_MSG
+ depends on NET
+
+config QCOM_PDR_MSG
+ tristate
+
+config QCOM_QMI_HELPERS
+ tristate
+ depends on NET
config QCOM_UBWC_CONFIG
tristate
+ depends on QCOM_SMEM
help
Most Qualcomm SoCs feature a number of Universal Bandwidth Compression
(UBWC) engines across various IP blocks, which need to be initialized
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index b7f1d2a57367..798643be3590 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -23,7 +23,8 @@ obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o
qcom_rpmh-y += rpmh-rsc.o
qcom_rpmh-y += rpmh.o
obj-$(CONFIG_QCOM_SMD_RPM) += rpm-proc.o smd-rpm.o
-obj-$(CONFIG_QCOM_SMEM) += smem.o
+qcom_smem-y += smem.o smem_dramc.o
+obj-$(CONFIG_QCOM_SMEM) += qcom_smem.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
CFLAGS_smp2p.o := -I$(src)
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index 84a75d8c4b70..3f1eee784387 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index b203bc685cad..e256595b9826 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/xarray.h>
#include <linux/firmware/qcom/qcom_scm.h>
@@ -108,11 +109,15 @@ struct qcom_ice {
void __iomem *base;
struct clk *core_clk;
+ struct clk *iface_clk;
bool use_hwkm;
bool hwkm_init_complete;
u8 hwkm_version;
};
+static DEFINE_XARRAY(ice_handles);
+static DEFINE_MUTEX(ice_mutex);
+
static bool qcom_ice_check_supported(struct qcom_ice *ice)
{
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
@@ -121,8 +126,8 @@ static bool qcom_ice_check_supported(struct qcom_ice *ice)
int minor = FIELD_GET(GENMASK(23, 16), regval);
int step = FIELD_GET(GENMASK(15, 0), regval);
- /* For now this driver only supports ICE version 3 and 4. */
- if (major != 3 && major != 4) {
+ /* Support ICE version 3 and above. */
+ if (major < 3) {
dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
major, minor, step);
return false;
@@ -312,8 +317,13 @@ int qcom_ice_resume(struct qcom_ice *ice)
err = clk_prepare_enable(ice->core_clk);
if (err) {
- dev_err(dev, "failed to enable core clock (%d)\n",
- err);
+ dev_err(dev, "Failed to enable core clock: %d\n", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(ice->iface_clk);
+ if (err) {
+ dev_err(dev, "Failed to enable iface clock: %d\n", err);
return err;
}
qcom_ice_hwkm_init(ice);
@@ -323,6 +333,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume);
int qcom_ice_suspend(struct qcom_ice *ice)
{
+ clk_disable_unprepare(ice->iface_clk);
clk_disable_unprepare(ice->core_clk);
ice->hwkm_init_complete = false;
@@ -559,7 +570,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
if (!qcom_scm_ice_available()) {
dev_warn(dev, "ICE SCM interface not found\n");
- return NULL;
+ return ERR_PTR(-EOPNOTSUPP);
}
engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL);
@@ -580,10 +591,16 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
if (!engine->core_clk)
engine->core_clk = devm_clk_get_optional_enabled(dev, "ice");
if (!engine->core_clk)
+ engine->core_clk = devm_clk_get_optional_enabled(dev, "core");
+ if (!engine->core_clk)
engine->core_clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(engine->core_clk))
return ERR_CAST(engine->core_clk);
+ engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface");
+ if (IS_ERR(engine->iface_clk))
+ return ERR_CAST(engine->iface_clk);
+
if (!qcom_ice_check_supported(engine))
return ERR_PTR(-EOPNOTSUPP);
@@ -631,6 +648,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
return qcom_ice_create(&pdev->dev, base);
}
+ guard(mutex)(&ice_mutex);
+
/*
* If the consumer node does not provider an 'ice' reg range
* (legacy DT binding), then it must at least provide a phandle
@@ -639,20 +658,21 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node,
"qcom,ice", 0);
if (!node)
- return NULL;
+ return ERR_PTR(-EOPNOTSUPP);
pdev = of_find_device_by_node(node);
if (!pdev) {
dev_err(dev, "Cannot find device node %s\n", node->name);
- return ERR_PTR(-EPROBE_DEFER);
+ return ERR_PTR(-ENODEV);
}
- ice = platform_get_drvdata(pdev);
- if (!ice) {
- dev_err(dev, "Cannot get ice instance from %s\n",
- dev_name(&pdev->dev));
+ ice = xa_load(&ice_handles, pdev->dev.of_node->phandle);
+ if (IS_ERR_OR_NULL(ice)) {
platform_device_put(pdev);
- return ERR_PTR(-EPROBE_DEFER);
+ if (!ice)
+ return ERR_PTR(-EPROBE_DEFER);
+ else
+ return ice;
}
link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
@@ -691,8 +711,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res)
* phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already
* be created and so this function will return that instead.
*
- * Return: ICE pointer on success, NULL if there is no ICE data provided by the
- * consumer or ERR_PTR() on error.
+ * Return: ICE pointer on success, ERR_PTR() on error.
*/
struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
{
@@ -703,7 +722,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
return ERR_PTR(-ENOMEM);
ice = of_qcom_ice_get(dev);
- if (!IS_ERR_OR_NULL(ice)) {
+ if (!IS_ERR(ice)) {
*dr = ice;
devres_add(dev, dr);
} else {
@@ -716,24 +735,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get);
static int qcom_ice_probe(struct platform_device *pdev)
{
+ unsigned long phandle = pdev->dev.of_node->phandle;
struct qcom_ice *engine;
void __iomem *base;
+ guard(mutex)(&ice_mutex);
+
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_warn(&pdev->dev, "ICE registers not found\n");
+ /* Store the error pointer for devm_of_qcom_ice_get() */
+ xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL);
return PTR_ERR(base);
}
engine = qcom_ice_create(&pdev->dev, base);
- if (IS_ERR(engine))
+ if (IS_ERR(engine)) {
+ /* Store the error pointer for devm_of_qcom_ice_get() */
+ xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
return PTR_ERR(engine);
+ }
- platform_set_drvdata(pdev, engine);
+ xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
return 0;
}
+static void qcom_ice_remove(struct platform_device *pdev)
+{
+ unsigned long phandle = pdev->dev.of_node->phandle;
+
+ guard(mutex)(&ice_mutex);
+ xa_store(&ice_handles, phandle, NULL, GFP_KERNEL);
+}
+
static const struct of_device_id qcom_ice_of_match_table[] = {
{ .compatible = "qcom,inline-crypto-engine" },
{ },
@@ -742,6 +777,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table);
static struct platform_driver qcom_ice_driver = {
.probe = qcom_ice_probe,
+ .remove = qcom_ice_remove,
.driver = {
.name = "qcom-ice",
.of_match_table = qcom_ice_of_match_table,
diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 0161ceec8842..733999867bbf 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -5,7 +5,6 @@
*/
#include <linux/bitfield.h>
-#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/io.h>
@@ -14,6 +13,7 @@
#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/slab.h>
@@ -76,10 +76,16 @@
#define LLCC_VERSION_4_1_0_0 0x04010000
#define LLCC_VERSION_6_0_0_0 0X06000000
+#define SLC_SCT_MEM_LAYOUT_VERSION1 1 /* SCT Memory layout version */
+#define SLC_SCT_DONE 0x00534354444f4e45 /* SCT programming OK */
+#define SLC_SCT_FAIL 0x005343544641494c /* SCT programming failed */
+#define SLC_SCT_NAME_LEN 15
+#define SLC_SCT_SLICE_ACT_ON_BOOT BIT(25)
+
/**
- * struct llcc_slice_config - Data associated with the llcc slice
+ * struct llcc_slice_config - Data associated with the LLCC slice
* @usecase_id: Unique id for the client's use case
- * @slice_id: llcc slice id for each client
+ * @slice_id: LLCC slice id for each client
* @max_cap: The maximum capacity of the cache slice provided in KB
* @priority: Priority of the client used to select victim line for replacement
* @fixed_size: Boolean indicating if the slice has a fixed capacity
@@ -93,7 +99,7 @@
* slice: normal or TCM(Tightly Coupled Memory)
* @probe_target_ways: Determines what ways to probe for access hit. When
* configured to 1 only bonus and reserved ways are probed.
- * When configured to 0 all ways in llcc are probed.
+ * When configured to 0 all ways in LLCC are probed.
* @dis_cap_alloc: Disable capacity based allocation for a client
* @retain_on_pc: If this bit is set and client has maintained active vote
* then the ways assigned to this client are not flushed on power
@@ -143,6 +149,87 @@ struct llcc_slice_config {
u32 parent_slice_id;
};
+/*
+ * struct slc_sct_error - Represents SCT error
+ * @code: FW code status
+ * @param: Holds the SCT programming error
+ */
+struct slc_sct_error {
+ __le64 code;
+ __le64 param;
+} __packed;
+
+/*
+ * struct slc_sct_status - SCT programming status
+ * @program_status: Indicates programming success or failure
+ * @version: SCT mem layout version
+ * @error: Error enum and its param
+ */
+struct slc_sct_status {
+ __le64 program_status;
+ /* Use the lower 8 bits */
+ __le64 version;
+ struct slc_sct_error error;
+} __packed;
+
+/*
+ * struct slc_sct_details - SCT details
+ * @revision: revision of the SCT table
+ * @name: name of the SCT table
+ */
+struct slc_sct_details {
+ u8 revision;
+ char name[SLC_SCT_NAME_LEN];
+} __packed;
+
+/*
+ * struct tcm_mem_info - SC TCM Shared memory details
+ * @is_present: is TCM region present
+ * @offset: offset of TCM shared memory details
+ */
+struct slc_tcm_mem_info {
+ __le32 is_present;
+ __le32 offset;
+} __packed;
+
+/*
+ * struct slc_sct_slice_desc - Slice descriptor definition used in shmem
+ * @slice_id: SCID of the slice
+ * @usecase_id: Usecase ID of the slice
+ * @slice_properties:
+ * slice_size: Contains the slice descriptor size - 20 bit wide
+ * rsvd: Reserved space - 4 bit wide
+ * flags: Flags for descriptors - 3 bit wide
+ * MPAM SCID: Bit 24
+ * Activate on boot: Bit 25
+ * Non-HLOS SCID: Bit 26
+ * HWMutex: Ensures only one processor (CPU or MCU) at a time can
+ * access the LLCC hardware resources - 5 bit wide
+ */
+struct slc_sct_slice_desc {
+ __le16 slice_id;
+ __le16 usecase_id;
+ __le32 slice_properties;
+} __packed;
+
+/*
+ * struct slc_sct_mem - Shared memory structure
+ * @sct_status: Status of SCT programming
+ * @sct_details: Sct revision and name details
+ * @tcm_mem_info: TCM shared memory presence & offset info
+ * @slice_descs_count: Number of slice desc present in SCT
+ * @scid_max: Maximum no. of SCIDs supported
+ * @slice_descs: Array of SCT slice desc
+ */
+struct slc_sct_mem {
+ struct slc_sct_status sct_status;
+ struct slc_sct_details sct_details;
+ struct slc_tcm_mem_info tcm_mem_info;
+ __le32 slice_descs_count;
+ __le32 scid_max;
+ struct slc_sct_slice_desc slice_descs[] __counted_by_le(slice_descs_count);
+} __packed;
+
struct qcom_llcc_config {
const struct llcc_slice_config *sct_data;
const u32 *reg_offset;
@@ -181,6 +268,171 @@ enum llcc_reg_offset {
LLCC_TRP_WRS_CACHEABLE_EN,
};
+static const struct llcc_slice_config eliza_data[] = {
+ {
+ .usecase_id = LLCC_CPUSS,
+ .slice_id = 1,
+ .max_cap = 896,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .write_scid_en = true,
+ .stale_en = true,
+ },
+ {
+ .usecase_id = LLCC_MDMHPFX,
+ .slice_id = 24,
+ .max_cap = 1024,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_VIDSC0,
+ .slice_id = 2,
+ .max_cap = 128,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_MDMHPGRW,
+ .slice_id = 25,
+ .max_cap = 1024,
+ .priority = 5,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_GPUHTW,
+ .slice_id = 11,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_GPU,
+ .slice_id = 9,
+ .max_cap = 896,
+ .priority = 1,
+ .bonus_ways = 0xfff,
+ .write_scid_cacheable_en = true,
+ },
+ {
+ .usecase_id = LLCC_MMUHWT,
+ .slice_id = 18,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ },
+ {
+ .usecase_id = LLCC_MDMPNG,
+ .slice_id = 27,
+ .max_cap = 256,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_MODPE,
+ .slice_id = 29,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xf00,
+ .alloc_oneway_en = true,
+ },
+ {
+ .usecase_id = LLCC_WRCACHE,
+ .slice_id = 31,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ },
+ {
+ .usecase_id = LLCC_LCPDARE,
+ .slice_id = 30,
+ .max_cap = 128,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .alloc_oneway_en = true,
+ },
+ {
+ .usecase_id = LLCC_ISLAND1,
+ .slice_id = 12,
+ .max_cap = 1280,
+ .priority = 7,
+ .fixed_size = true,
+ .res_ways = 0x3ff,
+ },
+ {
+ .usecase_id = LLCC_CAMOFE,
+ .slice_id = 33,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMRTIP,
+ .slice_id = 13,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMSRTIP,
+ .slice_id = 14,
+ .max_cap = 512,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMRTRF,
+ .slice_id = 7,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMSRTRF,
+ .slice_id = 21,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CPUSSMPAM,
+ .slice_id = 6,
+ .max_cap = 512,
+ .priority = 0,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .write_scid_en = true,
+ .stale_en = true,
+ },
+};
+
static const struct llcc_slice_config glymur_data[] = {
{
.usecase_id = LLCC_CPUSS,
@@ -2053,6 +2305,20 @@ static const struct llcc_slice_config sdm845_data[] = {{
},
};
+static const struct llcc_slice_config shikra_data[] = {
+ {
+ .usecase_id = LLCC_ECC,
+ .slice_id = 26,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0x3,
+ .cache_mode = 0,
+ .activate_on_init = true,
+ .vict_prio = true,
+ },
+};
+
static const struct llcc_slice_config sm6350_data[] = {
{
.usecase_id = LLCC_CPUSS,
@@ -4141,6 +4407,24 @@ static const u32 llcc_v6_reg_offset[] = {
[LLCC_TRP_WRS_CACHEABLE_EN] = 0x00042088,
};
+static const struct qcom_llcc_config hawi_sct_cfg[] = {
+ {
+ .sct_data = NULL,
+ .size = 0,
+ .reg_offset = llcc_v6_reg_offset,
+ .edac_reg_offset = &llcc_v6_edac_reg_offset,
+ },
+};
+
+static const struct qcom_llcc_config eliza_cfg[] = {
+ {
+ .sct_data = eliza_data,
+ .size = ARRAY_SIZE(eliza_data),
+ .reg_offset = llcc_v6_reg_offset,
+ .edac_reg_offset = &llcc_v6_edac_reg_offset,
+ },
+};
+
static const struct qcom_llcc_config kaanapali_cfg[] = {
{
.sct_data = kaanapali_data,
@@ -4305,6 +4589,16 @@ static const struct qcom_llcc_config sdm845_cfg[] = {
},
};
+static const struct qcom_llcc_config shikra_cfg[] = {
+ {
+ .sct_data = shikra_data,
+ .size = ARRAY_SIZE(shikra_data),
+ .reg_offset = llcc_v2_1_reg_offset,
+ .edac_reg_offset = &llcc_v2_1_edac_reg_offset,
+ .irq_configured = true,
+ },
+};
+
static const struct qcom_llcc_config sm6350_cfg[] = {
{
.sct_data = sm6350_data,
@@ -4397,6 +4691,16 @@ static const struct qcom_llcc_config x1e80100_cfg[] = {
},
};
+static const struct qcom_sct_config hawi_sct_cfgs = {
+ .llcc_config = hawi_sct_cfg,
+ .num_config = ARRAY_SIZE(hawi_sct_cfg),
+};
+
+static const struct qcom_sct_config eliza_cfgs = {
+ .llcc_config = eliza_cfg,
+ .num_config = ARRAY_SIZE(eliza_cfg),
+};
+
static const struct qcom_sct_config kaanapali_cfgs = {
.llcc_config = kaanapali_cfg,
.num_config = ARRAY_SIZE(kaanapali_cfg),
@@ -4472,6 +4776,11 @@ static const struct qcom_sct_config sdm845_cfgs = {
.num_config = ARRAY_SIZE(sdm845_cfg),
};
+static const struct qcom_sct_config shikra_cfgs = {
+ .llcc_config = shikra_cfg,
+ .num_config = ARRAY_SIZE(shikra_cfg),
+};
+
static const struct qcom_sct_config sm6350_cfgs = {
.llcc_config = sm6350_cfg,
.num_config = ARRAY_SIZE(sm6350_cfg),
@@ -4525,37 +4834,34 @@ static const struct qcom_sct_config x1e80100_cfgs = {
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
/**
- * llcc_slice_getd - get llcc slice descriptor
+ * llcc_slice_getd - get LLCC slice descriptor
* @uid: usecase_id for the client
*
- * A pointer to llcc slice descriptor will be returned on success
+ * A pointer to LLCC slice descriptor will be returned on success
* and error pointer is returned on failure
*/
struct llcc_slice_desc *llcc_slice_getd(u32 uid)
{
- const struct llcc_slice_config *cfg;
- u32 sz, i;
-
if (IS_ERR(drv_data))
return ERR_CAST(drv_data);
- cfg = drv_data->cfg;
- sz = drv_data->cfg_size;
+ if (IS_ERR_OR_NULL(drv_data->desc))
+ return ERR_PTR(-ENODEV);
- for (i = 0; cfg && i < sz; i++, cfg++)
- if (cfg->usecase_id == uid)
- break;
+ for (u32 i = 0; i < drv_data->cfg_size; i++) {
+ if (uid == drv_data->desc[i].uid)
+ return &drv_data->desc[i];
+ }
- if (i == sz)
- return ERR_PTR(-ENODEV);
+ dev_err(drv_data->dev, "Failed to get slice desc for uid: %u\n", uid);
- return &drv_data->desc[i];
+ return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL_GPL(llcc_slice_getd);
/**
- * llcc_slice_putd - llcc slice descriptor
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_putd - LLCC slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
void llcc_slice_putd(struct llcc_slice_desc *desc)
{
@@ -4618,8 +4924,8 @@ static int llcc_update_act_ctrl(u32 sid,
}
/**
- * llcc_slice_activate - Activate the llcc slice
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_activate - Activate the LLCC slice
+ * @desc: Pointer to LLCC slice descriptor
*
* A value of zero will be returned on success and a negative errno will
* be returned in error cases
@@ -4654,8 +4960,8 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
EXPORT_SYMBOL_GPL(llcc_slice_activate);
/**
- * llcc_slice_deactivate - Deactivate the llcc slice
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_deactivate - Deactivate the LLCC slice
+ * @desc: Pointer to LLCC slice descriptor
*
* A value of zero will be returned on success and a negative errno will
* be returned in error cases
@@ -4691,7 +4997,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
/**
* llcc_get_slice_id - return the slice id
- * @desc: Pointer to llcc slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
int llcc_get_slice_id(struct llcc_slice_desc *desc)
{
@@ -4704,7 +5010,7 @@ EXPORT_SYMBOL_GPL(llcc_get_slice_id);
/**
* llcc_get_slice_size - return the slice id
- * @desc: Pointer to llcc slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
{
@@ -4738,9 +5044,9 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
/*
* LLCC instances can vary for each target.
* The SW writes to broadcast register which gets propagated
- * to each llcc instance (llcc0,.. llccN).
+ * to each LLCC instance (llcc0,.. llccN).
* Since the size of the memory is divided equally amongst the
- * llcc instances, we need to configure the max cap accordingly.
+ * LLCC instances, we need to configure the max cap accordingly.
*/
max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
@@ -5029,6 +5335,12 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev,
sz = drv_data->cfg_size;
llcc_table = drv_data->cfg;
+ for (i = 0; i < sz; i++) {
+ drv_data->desc[i].uid = llcc_table[i].usecase_id;
+ drv_data->desc[i].slice_id = llcc_table[i].slice_id;
+ drv_data->desc[i].slice_size = llcc_table[i].max_cap;
+ }
+
if (drv_data->version >= LLCC_VERSION_6_0_0_0) {
for (i = 0; i < sz; i++) {
ret = _qcom_llcc_cfg_program_v6(&llcc_table[i], cfg);
@@ -5064,6 +5376,101 @@ static int qcom_llcc_get_cfg_index(struct platform_device *pdev, u8 *cfg_index,
return ret;
}
+static int qcom_llcc_verify_fw_config(struct device *dev,
+ const struct slc_sct_mem *slc_mem)
+{
+ u64 program_status;
+
+ program_status = le64_to_cpu(slc_mem->sct_status.program_status);
+
+ if (program_status == SLC_SCT_DONE) {
+ u32 desc_count = le32_to_cpu(slc_mem->slice_descs_count);
+ u32 scid_max = le32_to_cpu(slc_mem->scid_max);
+
+ if (desc_count > scid_max) {
+ dev_err(dev, "Descriptor count above max limit (%u > %u)\n",
+ desc_count, scid_max);
+ return -EINVAL;
+ }
+
+ u8 revision = slc_mem->sct_details.revision;
+ char name_buf[SLC_SCT_NAME_LEN];
+
+ memcpy(name_buf, slc_mem->sct_details.name,
+ SLC_SCT_NAME_LEN - 1);
+ name_buf[SLC_SCT_NAME_LEN - 1] = '\0';
+
+ dev_dbg(dev, "SCT init: desc_count=%u, rev=%u, name=%s\n",
+ desc_count, revision, name_buf);
+
+ return 0;
+ } else if (program_status == SLC_SCT_FAIL) {
+ u8 version = (u8)(le64_to_cpu(slc_mem->sct_status.version));
+ u64 code = le64_to_cpu(slc_mem->sct_status.error.code);
+ u64 param = le64_to_cpu(slc_mem->sct_status.error.param);
+
+ if (version == SLC_SCT_MEM_LAYOUT_VERSION1) {
+ dev_err(dev, "SCT init failed: code = %llu, param = %llu, version = 0x%x\n",
+ code, param, version);
+ } else {
+ dev_err(dev, "Found unsupported version %u\n", version);
+ }
+ } else {
+ dev_err(dev, "Unknown SCT Initialization error\n");
+ }
+
+ return -EINVAL;
+}
+
+static int qcom_llcc_get_fw_config(struct platform_device *pdev)
+{
+ const struct slc_sct_mem *slc_mem = NULL;
+ const struct slc_sct_slice_desc *memslice;
+ struct device *dev = &pdev->dev;
+ u32 slice_properties;
+ struct resource res;
+ u32 i, sz;
+ int ret;
+
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (ret) {
+ dev_err(dev, "Unable to locate DT /reserved-memory resource\n");
+ return ret;
+ }
+
+ slc_mem = devm_memremap(dev, res.start, resource_size(&res), MEMREMAP_WB);
+ if (IS_ERR(slc_mem)) {
+ dev_err(dev, "Failed to memremap SLC shared memory\n");
+ return PTR_ERR(slc_mem);
+ }
+
+ ret = qcom_llcc_verify_fw_config(dev, slc_mem);
+ if (ret)
+ return ret;
+
+ sz = le32_to_cpu(slc_mem->slice_descs_count);
+
+ drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc),
+ GFP_KERNEL);
+ if (!drv_data->desc)
+ return -ENOMEM;
+
+ for (i = 0; i < sz; i++) {
+ memslice = &slc_mem->slice_descs[i];
+ drv_data->desc[i].slice_id = le16_to_cpu(memslice->slice_id);
+ drv_data->desc[i].uid = le16_to_cpu(memslice->usecase_id);
+ slice_properties = le32_to_cpu(memslice->slice_properties);
+ /* Set refcount to 1 if FW already activated this descriptor */
+ if (FIELD_GET(SLC_SCT_SLICE_ACT_ON_BOOT, slice_properties))
+ refcount_set(&drv_data->desc[i].refcount, 1);
+ }
+
+ drv_data->cfg = NULL;
+ drv_data->cfg_size = sz;
+
+ return 0;
+}
+
static void qcom_llcc_remove(struct platform_device *pdev)
{
/* Set the global pointer to a error code to avoid referencing it */
@@ -5096,8 +5503,6 @@ static int qcom_llcc_probe(struct platform_device *pdev)
struct platform_device *llcc_edac;
const struct qcom_sct_config *cfgs;
const struct qcom_llcc_config *cfg;
- const struct llcc_slice_config *llcc_cfg;
- u32 sz;
u8 cfg_index;
u32 version;
struct regmap *regmap;
@@ -5190,32 +5595,31 @@ static int qcom_llcc_probe(struct platform_device *pdev)
}
}
- llcc_cfg = cfg->sct_data;
- sz = cfg->size;
- drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc), GFP_KERNEL);
- if (!drv_data->desc) {
- ret = -ENOMEM;
- goto err;
- }
+ mutex_init(&drv_data->lock);
+ if (!cfg->size) {
+ ret = qcom_llcc_get_fw_config(pdev);
+ if (ret)
+ goto err;
+ } else {
+ drv_data->cfg = cfg->sct_data;
+ drv_data->cfg_size = cfg->size;
+ drv_data->desc = devm_kcalloc(dev, cfg->size,
+ sizeof(struct llcc_slice_desc), GFP_KERNEL);
- for (i = 0; i < sz; i++) {
- drv_data->desc[i].slice_id = llcc_cfg[i].slice_id;
- drv_data->desc[i].slice_size = llcc_cfg[i].max_cap;
- refcount_set(&drv_data->desc[i].refcount, 0);
+ if (!drv_data->desc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = qcom_llcc_cfg_program(pdev, cfg);
+ if (ret)
+ goto err;
}
- drv_data->cfg = llcc_cfg;
- drv_data->cfg_size = sz;
+ drv_data->ecc_irq = platform_get_irq_optional(pdev, 0);
drv_data->edac_reg_offset = cfg->edac_reg_offset;
drv_data->ecc_irq_configured = cfg->irq_configured;
- mutex_init(&drv_data->lock);
- platform_set_drvdata(pdev, drv_data);
-
- ret = qcom_llcc_cfg_program(pdev, cfg);
- if (ret)
- goto err;
-
- drv_data->ecc_irq = platform_get_irq_optional(pdev, 0);
+ drv_data->dev = dev;
/*
* On some platforms, the access to EDAC registers will be locked by
@@ -5228,9 +5632,11 @@ static int qcom_llcc_probe(struct platform_device *pdev)
"qcom_llcc_edac", -1, drv_data,
sizeof(*drv_data));
if (IS_ERR(llcc_edac))
- dev_err(dev, "Failed to register llcc edac driver\n");
+ dev_err(dev, "Failed to register LLCC EDAC driver\n");
}
+ platform_set_drvdata(pdev, drv_data);
+
return 0;
err:
drv_data = ERR_PTR(-ENODEV);
@@ -5238,7 +5644,9 @@ err:
}
static const struct of_device_id qcom_llcc_of_match[] = {
+ { .compatible = "qcom,eliza-llcc", .data = &eliza_cfgs },
{ .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs },
+ { .compatible = "qcom,hawi-llcc", .data = &hawi_sct_cfgs },
{ .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs},
{ .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs},
{ .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs},
@@ -5253,6 +5661,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs },
{ .compatible = "qcom,sdm670-llcc", .data = &sdm670_cfgs },
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs },
+ { .compatible = "qcom,shikra-llcc", .data = &shikra_cfgs },
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs },
{ .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs },
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfgs },
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index c004d444d698..002100fe2d32 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -11,9 +11,10 @@
#include <linux/device.h>
#include <linux/elf.h>
#include <linux/firmware.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -229,7 +230,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_context *ctx)
+ struct qcom_pas_context *ctx)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -271,7 +272,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
goto out;
}
- ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx);
+ ret = qcom_pas_init_image(pas_id, metadata, metadata_len, ctx);
kfree(metadata);
if (ret) {
/* Invalid firmware metadata */
@@ -280,7 +281,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
}
if (relocate) {
- ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
+ ret = qcom_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
if (ret) {
/* Unable to set up relocation */
dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name);
@@ -472,28 +473,34 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load);
* firmware segments (e.g., .bXX files). Authentication of the segments done
* by a separate call.
*
- * The PAS context must be initialized using qcom_scm_pas_context_init()
+ * The PAS context must be initialized using devm_qcom_pas_context_alloc()
* prior to invoking this function.
*
* @ctx: Pointer to the PAS (Peripheral Authentication Service) context
* @fw: Firmware object representing the .mdt file
* @firmware: Name of the firmware used to construct segment file names
- * @mem_region: Memory region allocated for loading the firmware
* @reloc_base: Physical address adjusted after relocation
*
* Return: 0 on success or a negative error code on failure.
*/
-int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
- const char *firmware, void *mem_region, phys_addr_t *reloc_base)
+int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw,
+ const char *firmware, phys_addr_t *reloc_base)
{
+ void __iomem *mem_region;
int ret;
ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx);
if (ret)
return ret;
- return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys,
- ctx->mem_size, reloc_base);
+ mem_region = qcom_pas_ctx_map(ctx);
+ if (!mem_region)
+ return -ENOMEM;
+
+ ret = qcom_mdt_load_no_init(ctx->dev, fw, firmware, (__force void *)mem_region,
+ ctx->mem_phys, ctx->mem_size, reloc_base);
+ iounmap(mem_region);
+ return ret;
}
EXPORT_SYMBOL_GPL(qcom_mdt_pas_load);
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
index 619bad2c27ee..13c434f8d03a 100644
--- a/drivers/soc/qcom/pmic_glink_altmode.c
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -53,7 +53,7 @@ struct usbc_sc8280x_tbt_data {
/* This field is NOP on USB4, all cables support rounded rates by spec */
u8 rounded_cable : 1;
u8 power_limited : 1;
- u8 res[11];
+ u8 res[7];
};
struct usbc_notify {
@@ -74,6 +74,7 @@ struct usbc_notify {
struct usbc_sc8280x_dp_data dp;
struct usbc_sc8280x_tbt_data tbt;
} extended_data;
+ u32 reserved;
};
struct usbc_sc8180x_notify {
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index cd1779b6a91a..873bfbd6b2b7 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -7,6 +7,11 @@
/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */
#define __DISABLE_TRACE_MMIO__
+#define CREATE_TRACE_POINTS
+#include <trace/events/qcom_geni_se.h>
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(geni_se_regs);
+
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
@@ -19,6 +24,8 @@
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
#include <linux/soc/qcom/geni-se.h>
/**
@@ -152,8 +159,6 @@ struct se_fw_hdr {
/*Magic numbers*/
#define SE_MAGIC_NUM 0x57464553
-#define MAX_GENI_CFG_RAMn_CNT 455
-
#define MI_PBT_NON_PAGED_SEGMENT 0x0
#define MI_PBT_HASH_SEGMENT 0x2
#define MI_PBT_NOTUSED_SEGMENT 0x3
@@ -280,6 +285,12 @@ struct se_fw_hdr {
#define geni_setbits32(_addr, _v) writel(readl(_addr) | (_v), _addr)
#define geni_clrbits32(_addr, _v) writel(readl(_addr) & ~(_v), _addr)
+enum domain_idx {
+ DOMAIN_IDX_POWER,
+ DOMAIN_IDX_PERF,
+ DOMAIN_IDX_MAX
+};
+
/**
* geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
* @se: Pointer to the corresponding serial engine.
@@ -582,6 +593,7 @@ static void geni_se_clks_off(struct geni_se *se)
clk_disable_unprepare(se->clk);
clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
+ clk_disable_unprepare(se->core_clk);
}
/**
@@ -618,7 +630,18 @@ static int geni_se_clks_on(struct geni_se *se)
ret = clk_prepare_enable(se->clk);
if (ret)
- clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
+ goto err_bulk_clks;
+
+ ret = clk_prepare_enable(se->core_clk);
+ if (ret)
+ goto err_se_clk;
+
+ return 0;
+
+err_se_clk:
+ clk_disable_unprepare(se->clk);
+err_bulk_clks:
+ clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
return ret;
}
@@ -899,30 +922,32 @@ EXPORT_SYMBOL_GPL(geni_se_rx_dma_unprep);
int geni_icc_get(struct geni_se *se, const char *icc_ddr)
{
- int i, err;
- const char *icc_names[] = {"qup-core", "qup-config", icc_ddr};
+ struct geni_icc_path *icc_paths = se->icc_paths;
if (has_acpi_companion(se->dev))
return 0;
- for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
- if (!icc_names[i])
- continue;
-
- se->icc_paths[i].path = devm_of_icc_get(se->dev, icc_names[i]);
- if (IS_ERR(se->icc_paths[i].path))
- goto err;
+ icc_paths[GENI_TO_CORE].path = devm_of_icc_get(se->dev, "qup-core");
+ if (IS_ERR(icc_paths[GENI_TO_CORE].path))
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[GENI_TO_CORE].path),
+ "Failed to get 'qup-core' ICC path\n");
+
+ icc_paths[CPU_TO_GENI].path = devm_of_icc_get(se->dev, "qup-config");
+ if (IS_ERR(icc_paths[CPU_TO_GENI].path))
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[CPU_TO_GENI].path),
+ "Failed to get 'qup-config' ICC path\n");
+
+ /* The DDR path is optional, depending on protocol and hw capabilities */
+ icc_paths[GENI_TO_DDR].path = devm_of_icc_get(se->dev, "qup-memory");
+ if (IS_ERR(icc_paths[GENI_TO_DDR].path)) {
+ if (PTR_ERR(icc_paths[GENI_TO_DDR].path) == -ENODATA)
+ icc_paths[GENI_TO_DDR].path = NULL;
+ else
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[GENI_TO_DDR].path),
+ "Failed to get 'qup-memory' ICC path\n");
}
return 0;
-
-err:
- err = PTR_ERR(se->icc_paths[i].path);
- if (err != -EPROBE_DEFER)
- dev_err_ratelimited(se->dev, "Failed to get ICC path '%s': %d\n",
- icc_names[i], err);
- return err;
-
}
EXPORT_SYMBOL_GPL(geni_icc_get);
@@ -944,6 +969,28 @@ int geni_icc_set_bw(struct geni_se *se)
}
EXPORT_SYMBOL_GPL(geni_icc_set_bw);
+/**
+ * geni_icc_set_bw_ab() - Set average bandwidth for all ICC paths and apply
+ * @se: Pointer to the concerned serial engine.
+ * @core_ab: Average bandwidth in kBps for GENI_TO_CORE path.
+ * @cfg_ab: Average bandwidth in kBps for CPU_TO_GENI path.
+ * @ddr_ab: Average bandwidth in kBps for GENI_TO_DDR path.
+ *
+ * Sets bandwidth values for all ICC paths and applies them. DDR path is
+ * optional and only set if it exists.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int geni_icc_set_bw_ab(struct geni_se *se, u32 core_ab, u32 cfg_ab, u32 ddr_ab)
+{
+ se->icc_paths[GENI_TO_CORE].avg_bw = core_ab;
+ se->icc_paths[CPU_TO_GENI].avg_bw = cfg_ab;
+ se->icc_paths[GENI_TO_DDR].avg_bw = ddr_ab;
+
+ return geni_icc_set_bw(se);
+}
+EXPORT_SYMBOL_GPL(geni_icc_set_bw_ab);
+
void geni_icc_set_tag(struct geni_se *se, u32 tag)
{
int i;
@@ -989,25 +1036,218 @@ int geni_icc_disable(struct geni_se *se)
EXPORT_SYMBOL_GPL(geni_icc_disable);
/**
+ * geni_se_resources_deactivate() - Deactivate GENI SE device resources
+ * @se: Pointer to the geni_se structure
+ *
+ * Deactivates device resources for power saving: OPP rate to 0, pin control
+ * to sleep state, turns off clocks, and disables interconnect. Skips ACPI devices.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int geni_se_resources_deactivate(struct geni_se *se)
+{
+ int ret;
+
+ if (has_acpi_companion(se->dev))
+ return 0;
+
+ if (se->has_opp)
+ dev_pm_opp_set_rate(se->dev, 0);
+
+ ret = pinctrl_pm_select_sleep_state(se->dev);
+ if (ret)
+ return ret;
+
+ geni_se_clks_off(se);
+
+ return geni_icc_disable(se);
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_deactivate);
+
+/**
+ * geni_se_resources_activate() - Activate GENI SE device resources
+ * @se: Pointer to the geni_se structure
+ *
+ * Activates device resources for operation: enables interconnect, prepares clocks,
+ * and sets pin control to default state. Includes error cleanup. Skips ACPI devices.
+ *
+ * Unlike geni_se_resources_deactivate(), this function doesn't alter the
+ * connected genpds' performance states, which must be additionally handled.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int geni_se_resources_activate(struct geni_se *se)
+{
+ int ret;
+
+ if (has_acpi_companion(se->dev))
+ return 0;
+
+ ret = geni_icc_enable(se);
+ if (ret)
+ return ret;
+
+ ret = geni_se_clks_on(se);
+ if (ret)
+ goto out_icc_disable;
+
+ ret = pinctrl_pm_select_default_state(se->dev);
+ if (ret) {
+ geni_se_clks_off(se);
+ goto out_icc_disable;
+ }
+
+ return 0;
+
+out_icc_disable:
+ geni_icc_disable(se);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_activate);
+
+/**
+ * geni_se_set_perf_level() - Set performance level for GENI SE.
+ * @se: Pointer to the struct geni_se instance.
+ * @level: The desired performance level.
+ *
+ * Sets the performance level by directly calling dev_pm_opp_set_level
+ * on the performance device associated with the SE.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_set_perf_level(struct geni_se *se, unsigned long level)
+{
+ return dev_pm_opp_set_level(se->pd_list->pd_devs[DOMAIN_IDX_PERF], level);
+}
+EXPORT_SYMBOL_GPL(geni_se_set_perf_level);
+
+/**
+ * geni_se_set_perf_opp() - Set performance OPP for GENI SE by frequency.
+ * @se: Pointer to the struct geni_se instance.
+ * @clk_freq: The requested clock frequency.
+ *
+ * Finds the nearest operating performance point (OPP) for the given
+ * clock frequency and applies it to the SE's performance device.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_set_perf_opp(struct geni_se *se, unsigned long clk_freq)
+{
+ struct device *perf_dev = se->pd_list->pd_devs[DOMAIN_IDX_PERF];
+ struct dev_pm_opp *opp;
+ int ret;
+
+ opp = dev_pm_opp_find_freq_floor(perf_dev, &clk_freq);
+ if (IS_ERR(opp)) {
+ dev_err(se->dev, "failed to find opp for freq %lu\n", clk_freq);
+ return PTR_ERR(opp);
+ }
+
+ ret = dev_pm_opp_set_opp(perf_dev, opp);
+ dev_pm_opp_put(opp);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(geni_se_set_perf_opp);
+
+/**
+ * geni_se_domain_attach() - Attach power domains to a GENI SE device.
+ * @se: Pointer to the geni_se structure representing the GENI SE device.
+ *
+ * This function attaches the power domains ("power" and "perf") required
+ * in the SCMI auto-VM environment to the GENI Serial Engine device. It
+ * initializes se->pd_list with the attached domains.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_domain_attach(struct geni_se *se)
+{
+ struct dev_pm_domain_attach_data pd_data = {
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ .pd_names = (const char*[]) { "power", "perf" },
+ .num_pd_names = 2,
+ };
+ int ret;
+
+ ret = devm_pm_domain_attach_list(se->dev,
+ &pd_data, &se->pd_list);
+ if (ret == 0)
+ return -ENODEV;
+ else if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(geni_se_domain_attach);
+
+/**
+ * geni_se_resources_init() - Initialize resources for a GENI SE device.
+ * @se: Pointer to the geni_se structure representing the GENI SE device.
+ *
+ * This function initializes various resources required by the GENI Serial Engine
+ * (SE) device, including clock resources (core and SE clocks), interconnect
+ * paths for communication.
+ * It retrieves optional and mandatory clock resources, adds an OF-based
+ * operating performance point (OPP) table, and sets up interconnect paths
+ * with default bandwidths. The function also sets a flag (`has_opp`) to
+ * indicate whether OPP support is available for the device.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int geni_se_resources_init(struct geni_se *se)
+{
+ int ret;
+
+ se->core_clk = devm_clk_get_optional(se->dev, "core");
+ if (IS_ERR(se->core_clk))
+ return dev_err_probe(se->dev, PTR_ERR(se->core_clk),
+ "Failed to get optional core clk\n");
+
+ se->clk = devm_clk_get(se->dev, "se");
+ if (IS_ERR(se->clk) && !has_acpi_companion(se->dev))
+ return dev_err_probe(se->dev, PTR_ERR(se->clk),
+ "Failed to get SE clk\n");
+
+ ret = devm_pm_opp_set_clkname(se->dev, "se");
+ if (ret)
+ return ret;
+
+ ret = devm_pm_opp_of_add_table(se->dev);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(se->dev, ret, "Failed to add OPP table\n");
+
+ se->has_opp = (ret == 0);
+
+ ret = geni_icc_get(se, "qup-memory");
+ if (ret)
+ return ret;
+
+ return geni_icc_set_bw_ab(se, GENI_DEFAULT_BW, GENI_DEFAULT_BW, GENI_DEFAULT_BW);
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_init);
+
+/**
* geni_find_protocol_fw() - Locate and validate SE firmware for a protocol.
- * @dev: Pointer to the device structure.
+ * @se: Pointer to the serial engine structure.
* @fw: Pointer to the firmware image.
* @protocol: Expected serial engine protocol type.
*
* Identifies the appropriate firmware image or configuration required for a
- * specific communication protocol instance running on a Qualcomm GENI
- * controller.
+ * specific communication protocol instance running on a Qualcomm GENI
+ * controller. Validates the firmware size against the hardware PROG_RAM_DEPTH
+ * read from SE_HW_PARAM_2.
*
* Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise.
*/
-static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw,
+static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw,
enum geni_se_protocol_type protocol)
{
+ struct device *dev = se->dev;
const struct elf32_hdr *ehdr;
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
struct se_fw_hdr *sefw;
u32 fw_end, cfg_idx_end, cfg_val_end;
+ u32 prog_ram_depth;
u16 fw_size;
int i;
@@ -1066,10 +1306,11 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct
sefw->fw_size_in_items = cpu_to_le16(fw_size);
}
- if (fw_size >= MAX_GENI_CFG_RAMn_CNT) {
- dev_err(dev,
- "Firmware size (%u) exceeds max allowed RAMn count (%u)\n",
- fw_size, MAX_GENI_CFG_RAMn_CNT);
+ prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK,
+ readl_relaxed(se->base + SE_HW_PARAM_2));
+ if (fw_size >= prog_ram_depth) {
+ dev_err(dev, "Firmware size (%u) exceeds RAM size (%u)\n",
+ fw_size, prog_ram_depth);
continue;
}
@@ -1193,7 +1434,7 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw,
int ret;
struct se_fw_hdr *hdr;
- hdr = geni_find_protocol_fw(se->dev, fw, protocol);
+ hdr = geni_find_protocol_fw(se, fw, protocol);
if (!hdr)
return -EINVAL;
diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c
index c255662b8fc3..259c41f0c34e 100644
--- a/drivers/soc/qcom/qcom_aoss.c
+++ b/drivers/soc/qcom/qcom_aoss.c
@@ -381,7 +381,7 @@ static int qmp_cooling_device_add(struct qmp *qmp,
qmp_cdev->qmp = qmp;
qmp_cdev->state = !qmp_cdev_max_state;
qmp_cdev->name = cdev_name;
- qmp_cdev->cdev = devm_thermal_of_cooling_device_register
+ qmp_cdev->cdev = devm_thermal_of_child_cooling_device_register
(qmp->dev, node,
cdev_name,
qmp_cdev, &qmp_cooling_device_ops);
diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c
index 7bb14c20ab5d..64ca99d7fad9 100644
--- a/drivers/soc/qcom/qcom_pd_mapper.c
+++ b/drivers/soc/qcom/qcom_pd_mapper.c
@@ -7,7 +7,6 @@
#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>
@@ -266,6 +265,12 @@ static const struct qcom_pdm_domain_data adsp_charger_pd = {
.services = { NULL },
};
+static const struct qcom_pdm_domain_data adsp_ois_pd = {
+ .domain = "msm/adsp/ois_pd",
+ .instance_id = 74,
+ .services = { NULL, },
+};
+
static const struct qcom_pdm_domain_data adsp_root_pd = {
.domain = "msm/adsp/root_pd",
.instance_id = 74,
@@ -305,6 +310,24 @@ static const struct qcom_pdm_domain_data cdsp_root_pd = {
.services = { NULL },
};
+static const struct qcom_pdm_domain_data cdsp1_root_pd = {
+ .domain = "msm/cdsp1/root_pd",
+ .instance_id = 125,
+ .services = { NULL },
+};
+
+static const struct qcom_pdm_domain_data gpdsp_root_pd = {
+ .domain = "msm/gpdsp/root_pd",
+ .instance_id = 192,
+ .services = { NULL },
+};
+
+static const struct qcom_pdm_domain_data gpdsp1_root_pd = {
+ .domain = "msm/gpdsp1/root_pd",
+ .instance_id = 241,
+ .services = { NULL },
+};
+
static const struct qcom_pdm_domain_data slpi_root_pd = {
.domain = "msm/slpi/root_pd",
.instance_id = 90,
@@ -370,6 +393,7 @@ static const struct qcom_pdm_domain_data *glymur_domains[] = {
static const struct qcom_pdm_domain_data *kaanapali_domains[] = {
&adsp_audio_pd,
+ &adsp_ois_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
@@ -419,6 +443,24 @@ static const struct qcom_pdm_domain_data *qcs615_domains[] = {
NULL,
};
+static const struct qcom_pdm_domain_data *qcs8300_domains[] = {
+ &adsp_audio_pd,
+ &adsp_root_pd,
+ &cdsp_root_pd,
+ &gpdsp_root_pd,
+ NULL,
+};
+
+static const struct qcom_pdm_domain_data *sa8775p_domains[] = {
+ &adsp_audio_pd,
+ &adsp_root_pd,
+ &cdsp_root_pd,
+ &cdsp1_root_pd,
+ &gpdsp_root_pd,
+ &gpdsp1_root_pd,
+ NULL,
+};
+
static const struct qcom_pdm_domain_data *sc7180_domains[] = {
&adsp_audio_pd,
&adsp_root_pd_pdr,
@@ -581,6 +623,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = {
{ .compatible = "qcom,eliza", .data = sm8550_domains, },
{ .compatible = "qcom,apq8096", .data = msm8996_domains, },
{ .compatible = "qcom,glymur", .data = glymur_domains, },
+ { .compatible = "qcom,hawi", .data = kaanapali_domains, },
{ .compatible = "qcom,kaanapali", .data = kaanapali_domains, },
{ .compatible = "qcom,mahua", .data = glymur_domains, },
{ .compatible = "qcom,milos", .data = sm8550_domains, },
@@ -595,6 +638,8 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = {
{ .compatible = "qcom,qcm6490", .data = sc7280_domains, },
{ .compatible = "qcom,qcs404", .data = qcs404_domains, },
{ .compatible = "qcom,qcs615", .data = qcs615_domains, },
+ { .compatible = "qcom,qcs8300", .data = qcs8300_domains, },
+ { .compatible = "qcom,sa8775p", .data = sa8775p_domains, },
{ .compatible = "qcom,sc7180", .data = sc7180_domains, },
{ .compatible = "qcom,sc7280", .data = sc7280_domains, },
{ .compatible = "qcom,sc8180x", .data = sc8180x_domains, },
diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c
index 2e380faf9080..06923408bfce 100644
--- a/drivers/soc/qcom/qcom_stats.c
+++ b/drivers/soc/qcom/qcom_stats.c
@@ -20,13 +20,6 @@
#define RPM_DYNAMIC_ADDR 0x14
#define RPM_DYNAMIC_ADDR_MASK 0xFFFF
-#define STAT_TYPE_OFFSET 0x0
-#define COUNT_OFFSET 0x4
-#define LAST_ENTERED_AT_OFFSET 0x8
-#define LAST_EXITED_AT_OFFSET 0x10
-#define ACCUMULATED_OFFSET 0x18
-#define CLIENT_VOTES_OFFSET 0x20
-
#define DDR_STATS_MAGIC_KEY 0xA1157A75
#define DDR_STATS_MAX_NUM_MODES 20
#define DDR_STATS_MAGIC_KEY_ADDR 0x0
@@ -54,6 +47,8 @@ static const struct subsystem_data subsystems[] = {
{ "cdsp1", 607, 12 },
{ "gpdsp0", 607, 17 },
{ "gpdsp1", 607, 18 },
+ { "soccp", 607, 19 },
+ { "dcp", 607, 22 },
{ "slpi", 608, 3 },
{ "gpu", 609, 0 },
{ "display", 610, 0 },
@@ -138,7 +133,7 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused)
if (d->appended_stats_avail) {
struct appended_stats votes;
- memcpy_fromio(&votes, reg + CLIENT_VOTES_OFFSET, sizeof(votes));
+ memcpy_fromio(&votes, reg + sizeof(struct sleep_stats), sizeof(votes));
seq_printf(s, "Client Votes: %#x\n", votes.client_votes);
}
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index c6f7d5c9c493..f881af35ecfc 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -443,6 +443,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
int i;
unsigned long irq_status;
const struct tcs_request *req;
+ u32 reg;
irq_status = readl_relaxed(drv->tcs_base + drv->regs[RSC_DRV_IRQ_STATUS]);
@@ -453,6 +454,11 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
trace_rpmh_tx_done(drv, i, req);
+ if (req->is_read) {
+ reg = drv->regs[RSC_DRV_CMD_RESP_DATA];
+ req->cmds[0].data = read_tcs_reg(drv, reg, i);
+ }
+
/* Clear AMC trigger & enable modes and
* disable interrupt for this TCS
*/
@@ -493,13 +499,15 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
const struct tcs_request *msg)
{
u32 msgid;
- u32 cmd_msgid = CMD_MSGID_LEN | CMD_MSGID_WRITE;
+ u32 cmd_msgid = CMD_MSGID_LEN;
u32 cmd_enable = 0;
struct tcs_cmd *cmd;
int i, j;
/* Convert all commands to RR when the request has wait_for_compl set */
cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0;
+ if (!msg->is_read)
+ cmd_msgid |= CMD_MSGID_WRITE;
for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) {
cmd = &msg->cmds[i];
@@ -513,7 +521,8 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_MSGID], tcs_id, j, msgid);
write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], tcs_id, j, cmd->addr);
- write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, j, cmd->data);
+ if (!msg->is_read)
+ write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, j, cmd->data);
trace_rpmh_send_msg(drv, tcs_id, msg->state, j, msgid, cmd);
}
@@ -944,17 +953,30 @@ static int rpmh_rsc_pd_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
+static void rpmh_rsc_pd_detach(void *data)
+{
+ dev_pm_genpd_remove_notifier(data);
+}
+
static int rpmh_rsc_pd_attach(struct rsc_drv *drv, struct device *dev)
{
int ret;
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
drv->genpd_nb.notifier_call = rpmh_rsc_pd_callback;
ret = dev_pm_genpd_add_notifier(dev, &drv->genpd_nb);
if (ret)
- pm_runtime_disable(dev);
+ return ret;
- return ret;
+ return devm_add_action_or_reset(dev, rpmh_rsc_pd_detach, dev);
+}
+
+static void rpmh_rsc_cpu_pm_unregister(void *data)
+{
+ cpu_pm_unregister_notifier(data);
}
static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv)
@@ -1107,7 +1129,15 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
return ret;
} else {
drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback;
- cpu_pm_register_notifier(&drv->rsc_pm);
+ ret = cpu_pm_register_notifier(&drv->rsc_pm);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rpmh_rsc_cpu_pm_unregister,
+ &drv->rsc_pm);
+ if (ret)
+ return ret;
}
}
@@ -1122,13 +1152,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, drv);
drv->dev = &pdev->dev;
- ret = devm_of_platform_populate(&pdev->dev);
- if (ret && pdev->dev.pm_domain) {
- dev_pm_genpd_remove_notifier(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- }
-
- return ret;
+ return devm_of_platform_populate(&pdev->dev);
}
static const struct of_device_id rpmh_drv_match[] = {
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index ca37da3dc2b1..f881c4c757ec 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -175,6 +175,9 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state,
struct cache_req *req;
int i;
+ if (rpm_msg->msg.is_read)
+ goto send_data;
+
/* Cache the request in our store and link the payload */
for (i = 0; i < rpm_msg->msg.num_cmds; i++) {
req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]);
@@ -182,6 +185,7 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state,
return PTR_ERR(req);
}
+send_data:
if (state == RPMH_ACTIVE_ONLY_STATE) {
ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg);
} else {
@@ -194,7 +198,7 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state,
}
static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
- const struct tcs_cmd *cmd, u32 n)
+ const struct tcs_cmd *cmd, u32 n, bool is_read)
{
if (!cmd || !n || n > MAX_RPMH_PAYLOAD)
return -EINVAL;
@@ -204,11 +208,46 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
req->msg.state = state;
req->msg.cmds = req->cmd;
req->msg.num_cmds = n;
+ req->msg.is_read = is_read;
return 0;
}
/**
+ * rpmh_read: Read a resource value
+ *
+ * @dev: The device making the request
+ * @cmd: The payload having address of resource to read
+ *
+ * Reads the value for the resource address given in tcs_cmd->addr
+ * and returns the tcs_cmd->data filled with same.
+ *
+ * Context: May sleep. Do not call from atomic contexts.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int rpmh_read(const struct device *dev, struct tcs_cmd *cmd)
+{
+ DECLARE_COMPLETION_ONSTACK(compl);
+ DEFINE_RPMH_MSG_ONSTACK(dev, RPMH_ACTIVE_ONLY_STATE, &compl, rpm_msg);
+ int ret;
+
+ ret = __fill_rpmh_msg(&rpm_msg, RPMH_ACTIVE_ONLY_STATE, cmd, 1, true);
+ if (ret)
+ return ret;
+
+ ret = __rpmh_write(dev, RPMH_ACTIVE_ONLY_STATE, &rpm_msg);
+ if (ret)
+ return ret;
+
+ ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
+ cmd[0].data = rpm_msg.cmd[0].data;
+
+ return (ret > 0) ? 0 : -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rpmh_read);
+
+/**
* rpmh_write_async: Write a set of RPMH commands
*
* @dev: The device making the request
@@ -230,7 +269,7 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state,
return -ENOMEM;
rpm_msg->needs_free = true;
- ret = __fill_rpmh_msg(rpm_msg, state, cmd, n);
+ ret = __fill_rpmh_msg(rpm_msg, state, cmd, n, false);
if (ret) {
kfree(rpm_msg);
return ret;
@@ -257,7 +296,7 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
int ret;
- ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n);
+ ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n, false);
if (ret)
return ret;
@@ -352,7 +391,7 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
rpm_msgs = req->rpm_msgs;
for (i = 0; i < count; i++) {
- __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]);
+ __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i], false);
cmd += n[i];
}
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index d5c94b47f431..b5e7bd8c1512 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -4,6 +4,7 @@
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*/
+#include <linux/debugfs.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -16,6 +17,8 @@
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/socinfo.h>
+#include "smem.h"
+
/*
* The Qualcomm shared memory system is a allocate only heap structure that
* consists of one of more memory areas that can be accessed by the processors
@@ -85,9 +88,6 @@
/* Processor/host identifier for the global partition */
#define SMEM_GLOBAL_HOST 0xfffe
-/* Max number of processors/hosts in a system */
-#define SMEM_HOST_COUNT 25
-
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
* @command: current command to be executed
@@ -270,6 +270,7 @@ struct smem_region {
* @partitions: list of partitions of current processor/host
* @item_count: max accepted item number
* @socinfo: platform device pointer
+ * @debugfs_dir: directory for SMEM-related debugfs attributes
* @num_regions: number of @regions
* @regions: list of the memory regions defining the shared memory
*/
@@ -282,9 +283,11 @@ struct qcom_smem {
struct platform_device *socinfo;
struct smem_ptable *ptable;
struct smem_partition global_partition;
- struct smem_partition partitions[SMEM_HOST_COUNT];
+ struct xarray partitions;
+
+ struct dentry *debugfs_dir;
- unsigned num_regions;
+ unsigned int num_regions;
struct smem_region regions[] __counted_by(num_regions);
};
@@ -382,7 +385,7 @@ static struct qcom_smem *__smem = INIT_ERR_PTR(-EPROBE_DEFER);
int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
{
/* This function is for remote procs, so ignore SMEM_HOST_APPS */
- if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
+ if (host == SMEM_HOST_APPS || !xa_load(&__smem->partitions, host))
return -EINVAL;
return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
@@ -402,7 +405,7 @@ EXPORT_SYMBOL_GPL(qcom_smem_is_available);
static int qcom_smem_alloc_private(struct qcom_smem *smem,
struct smem_partition *part,
- unsigned item,
+ unsigned int item,
size_t size)
{
struct smem_private_entry *hdr, *end;
@@ -463,7 +466,7 @@ bad_canary:
}
static int qcom_smem_alloc_global(struct qcom_smem *smem,
- unsigned item,
+ unsigned int item,
size_t size)
{
struct smem_global_entry *entry;
@@ -506,7 +509,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
*
* Return: 0 on success, negative errno on failure.
*/
-int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
+int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size)
{
struct smem_partition *part;
unsigned long flags;
@@ -530,8 +533,8 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
if (ret)
return ret;
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ part = xa_load(&__smem->partitions, host);
+ if (part) {
ret = qcom_smem_alloc_private(__smem, part, item, size);
} else if (__smem->global_partition.virt_base) {
part = &__smem->global_partition;
@@ -547,7 +550,7 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
EXPORT_SYMBOL_GPL(qcom_smem_alloc);
static void *qcom_smem_get_global(struct qcom_smem *smem,
- unsigned item,
+ unsigned int item,
size_t *size)
{
struct smem_header *header;
@@ -556,7 +559,6 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
u64 entry_offset;
u32 e_size;
u32 aux_base;
- unsigned i;
header = smem->regions[0].virt_base;
entry = &header->toc[item];
@@ -565,7 +567,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
- for (i = 0; i < smem->num_regions; i++) {
+ for (unsigned int i = 0; i < smem->num_regions; i++) {
region = &smem->regions[i];
if ((u32)region->aux_base == aux_base || !aux_base) {
@@ -587,7 +589,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
static void *qcom_smem_get_private(struct qcom_smem *smem,
struct smem_partition *part,
- unsigned item,
+ unsigned int item,
size_t *size)
{
struct smem_private_entry *e, *end;
@@ -675,6 +677,30 @@ invalid_canary:
return ERR_PTR(-EINVAL);
}
+void *__qcom_smem_get(struct qcom_smem *smem, unsigned int host, unsigned int item, size_t *size)
+{
+ struct smem_partition *part;
+ void *ptr;
+
+ if (IS_ERR(smem))
+ return smem;
+
+ if (item >= smem->item_count)
+ return ERR_PTR(-EINVAL);
+
+ part = xa_load(&smem->partitions, host);
+ if (part) {
+ ptr = qcom_smem_get_private(smem, part, item, size);
+ } else if (smem->global_partition.virt_base) {
+ part = &smem->global_partition;
+ ptr = qcom_smem_get_private(smem, part, item, size);
+ } else {
+ ptr = qcom_smem_get_global(smem, item, size);
+ }
+
+ return ptr;
+}
+
/**
* qcom_smem_get() - resolve ptr of size of a smem item
* @host: the remote processor, or -1
@@ -686,28 +712,9 @@ invalid_canary:
*
* Return: a pointer to an SMEM item on success, ERR_PTR() on failure.
*/
-void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
+void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size)
{
- struct smem_partition *part;
- void *ptr;
-
- if (IS_ERR(__smem))
- return __smem;
-
- if (item >= __smem->item_count)
- return ERR_PTR(-EINVAL);
-
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
- ptr = qcom_smem_get_private(__smem, part, item, size);
- } else if (__smem->global_partition.virt_base) {
- part = &__smem->global_partition;
- ptr = qcom_smem_get_private(__smem, part, item, size);
- } else {
- ptr = qcom_smem_get_global(__smem, item, size);
- }
-
- return ptr;
+ return __qcom_smem_get(__smem, host, item, size);
}
EXPORT_SYMBOL_GPL(qcom_smem_get);
@@ -720,18 +727,18 @@ EXPORT_SYMBOL_GPL(qcom_smem_get);
*
* Return: number of available bytes on success, negative errno on failure.
*/
-int qcom_smem_get_free_space(unsigned host)
+int qcom_smem_get_free_space(unsigned int host)
{
struct smem_partition *part;
struct smem_partition_header *phdr;
struct smem_header *header;
- unsigned ret;
+ unsigned int ret;
if (IS_ERR(__smem))
return PTR_ERR(__smem);
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ part = xa_load(&__smem->partitions, host);
+ if (part) {
phdr = part->virt_base;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
@@ -774,12 +781,11 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
{
struct smem_partition *part;
struct smem_region *area;
+ unsigned long index;
u64 offset;
u32 i;
- for (i = 0; i < SMEM_HOST_COUNT; i++) {
- part = &__smem->partitions[i];
-
+ xa_for_each(&__smem->partitions, index, part) {
if (addr_in_range(part->virt_base, part->size, p)) {
offset = p - part->virt_base;
@@ -1016,16 +1022,20 @@ static int
qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
{
struct smem_partition_header *header;
+ struct smem_partition *part;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
u16 remote_host;
u16 host0, host1;
+ int ret;
int i;
ptable = qcom_smem_get_ptable(smem);
if (IS_ERR(ptable))
return PTR_ERR(ptable);
+ xa_init(&smem->partitions);
+
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
if (!le32_to_cpu(entry->offset))
@@ -1042,12 +1052,7 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
else
continue;
- if (remote_host >= SMEM_HOST_COUNT) {
- dev_err(smem->dev, "bad host %u\n", remote_host);
- return -EINVAL;
- }
-
- if (smem->partitions[remote_host].virt_base) {
+ if (xa_load(&smem->partitions, remote_host)) {
dev_err(smem->dev, "duplicate host %u\n", remote_host);
return -EINVAL;
}
@@ -1056,11 +1061,20 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
if (!header)
return -EINVAL;
- smem->partitions[remote_host].virt_base = (void __iomem *)header;
- smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
- le32_to_cpu(entry->offset);
- smem->partitions[remote_host].size = le32_to_cpu(entry->size);
- smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
+ part = devm_kzalloc(smem->dev, sizeof(struct smem_partition), GFP_KERNEL);
+ if (!part)
+ return -ENOMEM;
+
+ part->virt_base = (void __iomem *)header;
+ part->phys_base = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
+ part->size = le32_to_cpu(entry->size);
+ part->cacheline = le32_to_cpu(entry->cacheline);
+
+ ret = xa_insert(&smem->partitions, remote_host, part, GFP_KERNEL);
+ if (ret) {
+ dev_err(smem->dev, "fail to insert host %u\n", remote_host);
+ return ret;
+ }
}
return 0;
@@ -1229,26 +1243,33 @@ static int qcom_smem_probe(struct platform_device *pdev)
return -EINVAL;
}
- BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
if (ret < 0 && ret != -ENOENT)
return ret;
+ smem->debugfs_dir = smem_dram_parse(smem, smem->dev);
+
__smem = smem;
smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
PLATFORM_DEVID_NONE, NULL,
0);
- if (IS_ERR(smem->socinfo))
+ if (IS_ERR(smem->socinfo)) {
+ debugfs_remove_recursive(smem->debugfs_dir);
+
dev_dbg(&pdev->dev, "failed to register socinfo device\n");
+ }
return 0;
}
static void qcom_smem_remove(struct platform_device *pdev)
{
+ debugfs_remove_recursive(__smem->debugfs_dir);
+
platform_device_unregister(__smem->socinfo);
+ xa_destroy(&__smem->partitions);
/* Set to -EPROBE_DEFER to signal unprobed state */
__smem = ERR_PTR(-EPROBE_DEFER);
}
diff --git a/drivers/soc/qcom/smem.h b/drivers/soc/qcom/smem.h
new file mode 100644
index 000000000000..40523eb87231
--- /dev/null
+++ b/drivers/soc/qcom/smem.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __QCOM_SMEM_INTERNAL__
+#define __QCOM_SMEM_INTERNAL__
+
+#include <linux/device.h>
+
+struct qcom_smem;
+
+struct dentry *smem_dram_parse(struct qcom_smem *smem, struct device *dev);
+void *__qcom_smem_get(struct qcom_smem *smem, unsigned int host, unsigned int item, size_t *size);
+
+#endif
diff --git a/drivers/soc/qcom/smem_dramc.c b/drivers/soc/qcom/smem_dramc.c
new file mode 100644
index 000000000000..63ebe7f6db59
--- /dev/null
+++ b/drivers/soc/qcom/smem_dramc.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/units.h>
+
+#include "smem.h"
+
+#define SMEM_DDR_INFO_ID 603
+
+#define MAX_DDR_FREQ_NUM_V3 13
+#define MAX_DDR_FREQ_NUM_V5 14
+
+#define MAX_CHAN_NUM 8
+#define MAX_RANK_NUM 2
+
+#define DDR_HBB_MIN 13
+#define DDR_HBB_MAX 19
+
+#define MAX_SHUB_ENTRIES 8
+
+static struct smem_dram *__dram;
+
+enum ddr_info_version {
+ INFO_UNKNOWN,
+ INFO_V3,
+ INFO_V3_WITH_14_FREQS,
+ INFO_V4,
+ INFO_V5,
+ INFO_V5_WITH_6_REGIONS,
+ INFO_V6, /* INFO_V6 seems to only have shipped with 6 DDR regions, unlike V7 */
+ INFO_V7,
+ INFO_V7_WITH_6_REGIONS,
+};
+
+struct smem_dram {
+ unsigned long frequencies[MAX_DDR_FREQ_NUM_V5];
+ u32 num_frequencies;
+ u8 hbb;
+};
+
+enum ddr_type {
+ DDR_TYPE_NODDR = 0,
+ DDR_TYPE_LPDDR1 = 1,
+ DDR_TYPE_LPDDR2 = 2,
+ DDR_TYPE_PCDDR2 = 3,
+ DDR_TYPE_PCDDR3 = 4,
+ DDR_TYPE_LPDDR3 = 5,
+ DDR_TYPE_LPDDR4 = 6,
+ DDR_TYPE_LPDDR4X = 7,
+ DDR_TYPE_LPDDR5 = 8,
+ DDR_TYPE_LPDDR5X = 9,
+};
+
+/* The data structures below are NOT __packed on purpose! */
+
+/* Structs used across multiple versions */
+struct ddr_part_details {
+ __le16 revision_id1;
+ __le16 revision_id2;
+ __le16 width;
+ __le16 density;
+};
+
+struct ddr_freq_table {
+ __le32 freq_khz;
+ u8 enabled;
+};
+
+/* V3 */
+struct ddr_freq_plan_v3 {
+ struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3];
+ u8 num_ddr_freqs;
+ phys_addr_t clk_period_address;
+};
+
+struct ddr_details_v3 {
+ u8 manufacturer_id;
+ u8 device_type;
+ struct ddr_part_details ddr_params[MAX_CHAN_NUM];
+ struct ddr_freq_plan_v3 ddr_freq_tbl;
+ u8 num_channels;
+};
+
+/* Some V3 structs have an additional frequency level */
+struct ddr_freq_plan_v3_14freqs {
+ struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3 + 1];
+ u8 num_ddr_freqs;
+ phys_addr_t clk_period_address;
+};
+
+struct ddr_details_v3_14freqs {
+ u8 manufacturer_id;
+ u8 device_type;
+ struct ddr_part_details ddr_params[MAX_CHAN_NUM];
+ struct ddr_freq_plan_v3_14freqs ddr_freq_tbl;
+ u8 num_channels;
+};
+
+/* V4 */
+struct ddr_details_v4 {
+ u8 manufacturer_id;
+ u8 device_type;
+ struct ddr_part_details ddr_params[MAX_CHAN_NUM];
+ struct ddr_freq_plan_v3 ddr_freq_tbl;
+ u8 num_channels;
+ u8 num_ranks[MAX_CHAN_NUM];
+ u8 highest_bank_addr_bit[MAX_CHAN_NUM][MAX_RANK_NUM];
+};
+
+/* V5 */
+struct shub_freq_table {
+ u8 enable;
+ __le32 freq_khz;
+};
+
+struct shub_freq_plan_entry {
+ u8 num_shub_freqs;
+ struct shub_freq_table shub_freq[MAX_SHUB_ENTRIES];
+};
+
+struct ddr_xbl2quantum_smem_data {
+ phys_addr_t ssr_cookie_addr;
+ __le32 reserved[10];
+};
+
+struct ddr_freq_plan_v5 {
+ struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V5];
+ u8 num_ddr_freqs;
+ phys_addr_t clk_period_address;
+ __le32 max_nom_ddr_freq;
+};
+
+struct ddr_region_v5 {
+ __le64 start_address;
+ __le64 size;
+ __le64 mem_controller_address;
+ __le32 granule_size; /* MiB */
+ u8 ddr_rank;
+#define DDR_RANK_0 BIT(0)
+#define DDR_RANK_1 BIT(1)
+ u8 segments_start_index;
+ __le64 segments_start_offset;
+};
+
+struct ddr_regions_v5 {
+ __le32 ddr_region_num; /* We expect this to always be 4 or 6 */
+ __le64 ddr_rank0_size;
+ __le64 ddr_rank1_size;
+ __le64 ddr_cs0_start_addr;
+ __le64 ddr_cs1_start_addr;
+ __le32 highest_bank_addr_bit;
+ struct ddr_region_v5 ddr_region[] __counted_by_le(ddr_region_num);
+};
+
+struct ddr_details_v5 {
+ u8 manufacturer_id;
+ u8 device_type;
+ struct ddr_part_details ddr_params[MAX_CHAN_NUM];
+ struct ddr_freq_plan_v5 ddr_freq_tbl;
+ u8 num_channels;
+ u8 _padding;
+ struct ddr_regions_v5 ddr_regions;
+};
+
+/* V6 */
+struct ddr_misc_info_v6 {
+ __le32 dsf_version;
+ __le32 reserved[10];
+};
+
+/* V7 */
+struct ddr_details_v7 {
+ u8 manufacturer_id;
+ u8 device_type;
+ struct ddr_part_details ddr_params[MAX_CHAN_NUM];
+ struct ddr_freq_plan_v5 ddr_freq_tbl;
+ u8 num_channels;
+ u8 sct_config;
+ struct ddr_regions_v5 ddr_regions;
+};
+
+/**
+ * qcom_smem_dram_get_hbb(): Get the Highest bank address bit
+ *
+ * Context: Check qcom_smem_is_available() before calling this function.
+ * Because __dram * is initialized by smem_dram_parse(), which is in turn
+ * called from * qcom_smem_probe(), __dram will only be NULL if the data
+ * couldn't have been found/interpreted correctly.
+ *
+ * Return: highest bank bit on success, -ENODATA on failure.
+ */
+int qcom_smem_dram_get_hbb(void)
+{
+ if (!__dram || !__dram->hbb)
+ return -ENODATA;
+
+ if (__dram->hbb < DDR_HBB_MIN || __dram->hbb > DDR_HBB_MAX)
+ return -ENODATA;
+
+ return __dram->hbb;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_dram_get_hbb);
+
+static void smem_dram_parse_v3_data(struct smem_dram *dram, void *data)
+{
+ struct ddr_details_v3 *details = data;
+
+ for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) {
+ struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
+
+ if (freq_entry->freq_khz && freq_entry->enabled) {
+ u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
+ dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
+ }
+ }
+}
+
+static void smem_dram_parse_v3_14freqs_data(struct smem_dram *dram, void *data)
+{
+ struct ddr_details_v3_14freqs *details = data;
+
+ for (int i = 0; i < MAX_DDR_FREQ_NUM_V3 + 1; i++) {
+ struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
+
+ if (freq_entry->freq_khz && freq_entry->enabled)
+ dram->frequencies[dram->num_frequencies++] = 1000 * freq_entry->freq_khz;
+ }
+}
+
+static void smem_dram_parse_v4_data(struct smem_dram *dram, void *data)
+{
+ struct ddr_details_v4 *details = data;
+
+ /* Rank 0 channel 0 entry holds the correct value */
+ dram->hbb = details->highest_bank_addr_bit[0][0];
+
+ for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) {
+ struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
+
+ if (freq_entry->freq_khz && freq_entry->enabled) {
+ u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
+ dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
+ }
+ }
+}
+
+static void smem_dram_parse_v5_data(struct smem_dram *dram, void *data)
+{
+ struct ddr_details_v5 *details = data;
+ struct ddr_regions_v5 *region = &details->ddr_regions;
+
+ dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit);
+
+ for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) {
+ struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
+
+ if (freq_entry->freq_khz && freq_entry->enabled) {
+ u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
+ dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
+ }
+ }
+}
+
+static void smem_dram_parse_v7_data(struct smem_dram *dram, void *data)
+{
+ struct ddr_details_v7 *details = data;
+ struct ddr_regions_v5 *region = &details->ddr_regions;
+
+ dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit);
+
+ for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) {
+ struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i];
+
+ if (freq_entry->freq_khz && freq_entry->enabled) {
+ u32 freq_khz = le32_to_cpu(freq_entry->freq_khz);
+ dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz;
+ }
+ }
+}
+
+/* The structure contains no version field, so we have to perform some guesswork.. */
+static int smem_dram_infer_struct_version(size_t size)
+{
+ /* Some early versions provided less bytes of less useful data */
+ if (size < sizeof(struct ddr_details_v3))
+ return -EINVAL;
+
+ if (size == sizeof(struct ddr_details_v3))
+ return INFO_V3;
+
+ if (size == sizeof(struct ddr_details_v3_14freqs))
+ return INFO_V3_WITH_14_FREQS;
+
+ if (size == sizeof(struct ddr_details_v4))
+ return INFO_V4;
+
+ if (size == sizeof(struct ddr_details_v5) +
+ 4 * sizeof(struct ddr_region_v5))
+ return INFO_V5;
+
+ if (size == sizeof(struct ddr_details_v5) +
+ 4 * sizeof(struct ddr_region_v5) +
+ sizeof(struct ddr_xbl2quantum_smem_data) +
+ sizeof(struct shub_freq_plan_entry))
+ return INFO_V5;
+
+ if (size == sizeof(struct ddr_details_v5) +
+ 6 * sizeof(struct ddr_region_v5))
+ return INFO_V5_WITH_6_REGIONS;
+
+ if (size == sizeof(struct ddr_details_v5) +
+ 6 * sizeof(struct ddr_region_v5) +
+ sizeof(struct ddr_xbl2quantum_smem_data) +
+ sizeof(struct shub_freq_plan_entry))
+ return INFO_V5_WITH_6_REGIONS;
+
+ if (size == sizeof(struct ddr_details_v5) +
+ 6 * sizeof(struct ddr_region_v5) +
+ sizeof(struct ddr_misc_info_v6) +
+ sizeof(struct shub_freq_plan_entry))
+ return INFO_V6;
+
+ if (size == sizeof(struct ddr_details_v7) +
+ 4 * sizeof(struct ddr_region_v5) +
+ sizeof(struct ddr_misc_info_v6) +
+ sizeof(struct shub_freq_plan_entry))
+ return INFO_V7;
+
+ if (size == sizeof(struct ddr_details_v7) +
+ 6 * sizeof(struct ddr_region_v5) +
+ sizeof(struct ddr_misc_info_v6) +
+ sizeof(struct shub_freq_plan_entry))
+ return INFO_V7_WITH_6_REGIONS;
+
+ return INFO_UNKNOWN;
+}
+
+static int smem_dram_frequencies_show(struct seq_file *s, void *unused)
+{
+ struct smem_dram *dram = s->private;
+
+ for (int i = 0; i < dram->num_frequencies; i++)
+ seq_printf(s, "%lu\n", dram->frequencies[i]);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(smem_dram_frequencies);
+
+static int smem_hbb_show(struct seq_file *s, void *unused)
+{
+ struct smem_dram *dram = s->private;
+
+ if (!dram->hbb)
+ return -EINVAL;
+
+ seq_printf(s, "%d\n", dram->hbb);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(smem_hbb);
+
+struct dentry *smem_dram_parse(struct qcom_smem *smem, struct device *dev)
+{
+ struct dentry *debugfs_dir;
+ enum ddr_info_version ver;
+ struct smem_dram *dram;
+ size_t actual_size;
+ void *data;
+
+ /* No need to check qcom_smem_is_available(), this func is called by the SMEM driver */
+ data = __qcom_smem_get(smem, QCOM_SMEM_HOST_ANY, SMEM_DDR_INFO_ID, &actual_size);
+ if (IS_ERR_OR_NULL(data))
+ return ERR_PTR(-ENODATA);
+
+ ver = smem_dram_infer_struct_version(actual_size);
+ if (ver < 0) {
+ /* Some SoCs don't provide data that's useful for us */
+ return ERR_PTR(-ENODATA);
+ } else if (ver == INFO_UNKNOWN) {
+ /* In other cases, we may not have added support for a newer struct revision */
+ dev_err(dev, "Found an unknown type of DRAM info struct (size = %zu)\n",
+ actual_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ dram = devm_kzalloc(dev, sizeof(*dram), GFP_KERNEL);
+ if (!dram)
+ return ERR_PTR(-ENOMEM);
+
+ switch (ver) {
+ case INFO_V3:
+ smem_dram_parse_v3_data(dram, data);
+ break;
+ case INFO_V3_WITH_14_FREQS:
+ smem_dram_parse_v3_14freqs_data(dram, data);
+ break;
+ case INFO_V4:
+ smem_dram_parse_v4_data(dram, data);
+ break;
+ case INFO_V5:
+ case INFO_V5_WITH_6_REGIONS:
+ case INFO_V6:
+ smem_dram_parse_v5_data(dram, data);
+ break;
+ case INFO_V7:
+ case INFO_V7_WITH_6_REGIONS:
+ smem_dram_parse_v7_data(dram, data);
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ debugfs_dir = debugfs_create_dir("qcom_smem", NULL);
+ debugfs_create_file("dram_frequencies", 0444, debugfs_dir, dram,
+ &smem_dram_frequencies_fops);
+ debugfs_create_file("hbb", 0444, debugfs_dir, dram, &smem_hbb_fops);
+
+ __dram = dram;
+
+ return debugfs_dir;
+}
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 8ffd903ebddb..1836e226a294 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -188,7 +188,19 @@ static const char *const pmic_models[] = {
[80] = "PM7550",
[82] = "PMC8380",
[83] = "SMB2360",
+ [86] = "PM8750B",
+ [87] = "PMD8028",
+ [88] = "PMC1020H",
+ [89] = "PMIV0104",
+ [90] = "PMIV0102",
[91] = "PMIV0108",
+ [92] = "PMK8850",
+ [93] = "PMH0101",
+ [94] = "PMAU0102",
+ [95] = "SMB2370",
+ [96] = "PMH0104",
+ [97] = "PMH0110",
+ [98] = "PMCX0102",
};
struct socinfo_params {
@@ -413,6 +425,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ8078) },
{ qcom_board_id(SDM636) },
{ qcom_board_id(SDA636) },
+ { qcom_board_id(SDM850) },
{ qcom_board_id(SDM632) },
{ qcom_board_id(SDA632) },
{ qcom_board_id(SDA450) },
@@ -519,6 +532,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ5424) },
{ qcom_board_id(QCM6690) },
{ qcom_board_id(QCS6690) },
+ { qcom_board_id(SM7750) },
{ qcom_board_id(SM8850) },
{ qcom_board_id(IPQ5404) },
{ qcom_board_id(QCS9100) },
@@ -526,13 +540,23 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(QCS8275) },
{ qcom_board_id(QCS9075) },
{ qcom_board_id(QCS615) },
+ { qcom_board_id(SA8797P) },
{ qcom_board_id(CQ7790M) },
{ qcom_board_id(CQ7790S) },
+ { qcom_board_id(CQ2390M) },
+ { qcom_board_id(CQ2390S) },
+ { qcom_board_id(IQ2390S) },
{ qcom_board_id(IPQ5200) },
{ qcom_board_id(IPQ5210) },
{ qcom_board_id(QCF2200) },
{ qcom_board_id(QCF3200) },
{ qcom_board_id(QCF3210) },
+ { qcom_board_id(IPQ9620) },
+ { qcom_board_id(IPQ9650) },
+ { qcom_board_id(IPQ9610) },
+ { qcom_board_id(IPQ9630) },
+ { qcom_board_id(IPQ9640) },
+ { qcom_board_id(IPQ9670) },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)
diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c
index 3fe47d8f0f63..23901a4c51f7 100644
--- a/drivers/soc/qcom/ubwc_config.c
+++ b/drivers/soc/qcom/ubwc_config.c
@@ -11,298 +11,183 @@
#include <linux/platform_device.h>
#include <linux/soc/qcom/ubwc.h>
+#include <linux/soc/qcom/smem.h>
static const struct qcom_ubwc_cfg_data no_ubwc_data = {
/* no UBWC, no HBB */
};
-static const struct qcom_ubwc_cfg_data eliza_data = {
- .ubwc_enc_version = UBWC_5_0,
- .ubwc_dec_version = UBWC_5_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- /* TODO: highest_bank_bit = 14 for LP_DDR4 */
+static const struct qcom_ubwc_cfg_data ubwc_0_0_hbb15 = {
+ /* no UBWC */
.highest_bank_bit = 15,
- .macrotile_mode = true,
};
-static const struct qcom_ubwc_cfg_data kaanapali_data = {
- .ubwc_enc_version = UBWC_6_0,
- .ubwc_dec_version = UBWC_6_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 16,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data msm8937_data = {
+static const struct qcom_ubwc_cfg_data ubwc_1_0_hbb14 = {
.ubwc_enc_version = UBWC_1_0,
- .ubwc_dec_version = UBWC_1_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 |
- UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
.highest_bank_bit = 14,
};
-static const struct qcom_ubwc_cfg_data msm8998_data = {
+static const struct qcom_ubwc_cfg_data ubwc_1_0_hbb15 = {
.ubwc_enc_version = UBWC_1_0,
- .ubwc_dec_version = UBWC_1_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 |
- UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .highest_bank_bit = 15,
-};
-
-static const struct qcom_ubwc_cfg_data qcm2290_data = {
- /* no UBWC */
.highest_bank_bit = 15,
};
-static const struct qcom_ubwc_cfg_data sa8775p_data = {
- .ubwc_enc_version = UBWC_4_0,
- .ubwc_dec_version = UBWC_4_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 13,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data sar2130p_data = {
- .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */
- .ubwc_dec_version = UBWC_4_3,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 13,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data sc7180_data = {
+static const struct qcom_ubwc_cfg_data ubwc_2_0_hbb14 = {
.ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
.highest_bank_bit = 14,
};
-static const struct qcom_ubwc_cfg_data sc7280_data = {
- .ubwc_enc_version = UBWC_3_0,
- .ubwc_dec_version = UBWC_4_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 14,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data sc8180x_data = {
- .ubwc_enc_version = UBWC_3_0,
- .ubwc_dec_version = UBWC_3_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .highest_bank_bit = 16,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data sc8280xp_data = {
- .ubwc_enc_version = UBWC_4_0,
- .ubwc_dec_version = UBWC_4_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 16,
- .macrotile_mode = true,
-};
-
-static const struct qcom_ubwc_cfg_data sdm670_data = {
+static const struct qcom_ubwc_cfg_data ubwc_2_0_hbb15 = {
.ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .highest_bank_bit = 14,
-};
-
-static const struct qcom_ubwc_cfg_data sdm845_data = {
- .ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
.highest_bank_bit = 15,
};
-static const struct qcom_ubwc_cfg_data sm6115_data = {
- .ubwc_enc_version = UBWC_1_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 |
- UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 14,
+static const struct qcom_ubwc_cfg_data ubwc_3_0_hbb15 = {
+ .ubwc_enc_version = UBWC_3_0,
+ .highest_bank_bit = 15,
};
-static const struct qcom_ubwc_cfg_data sm6125_data = {
- .ubwc_enc_version = UBWC_1_0,
- .ubwc_dec_version = UBWC_3_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 |
- UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .highest_bank_bit = 14,
+static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb13 = {
+ .ubwc_enc_version = UBWC_3_1,
+ .highest_bank_bit = 13,
};
-static const struct qcom_ubwc_cfg_data sm6150_data = {
- .ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
+static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb14 = {
+ .ubwc_enc_version = UBWC_3_1,
.highest_bank_bit = 14,
};
-static const struct qcom_ubwc_cfg_data sm6350_data = {
- .ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- .highest_bank_bit = 14,
+static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb16 = {
+ .ubwc_enc_version = UBWC_3_1,
+ .highest_bank_bit = 16,
};
-static const struct qcom_ubwc_cfg_data sm7150_data = {
- .ubwc_enc_version = UBWC_2_0,
- .ubwc_dec_version = UBWC_2_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .highest_bank_bit = 14,
+static const struct qcom_ubwc_cfg_data ubwc_4_0_hbb16 = {
+ .ubwc_enc_version = UBWC_4_0,
+ .highest_bank_bit = 16,
};
-static const struct qcom_ubwc_cfg_data sm8150_data = {
- .ubwc_enc_version = UBWC_3_0,
- .ubwc_dec_version = UBWC_3_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
+static const struct qcom_ubwc_cfg_data ubwc_5_0_hbb15 = {
+ .ubwc_enc_version = UBWC_5_0,
+ /* TODO: highest_bank_bit = 14 for LP_DDR4 */
.highest_bank_bit = 15,
};
-static const struct qcom_ubwc_cfg_data sm8250_data = {
- .ubwc_enc_version = UBWC_4_0,
- .ubwc_dec_version = UBWC_4_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- /* TODO: highest_bank_bit = 15 for LP_DDR4 */
+static const struct qcom_ubwc_cfg_data ubwc_5_0_hbb16 = {
+ .ubwc_enc_version = UBWC_5_0,
.highest_bank_bit = 16,
- .macrotile_mode = true,
};
-static const struct qcom_ubwc_cfg_data sm8350_data = {
- .ubwc_enc_version = UBWC_4_0,
- .ubwc_dec_version = UBWC_4_0,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- /* TODO: highest_bank_bit = 15 for LP_DDR4 */
+static const struct qcom_ubwc_cfg_data ubwc_6_0_hbb16 = {
+ .ubwc_enc_version = UBWC_6_0,
.highest_bank_bit = 16,
- .macrotile_mode = true,
};
-static const struct qcom_ubwc_cfg_data sm8550_data = {
+static const struct qcom_ubwc_cfg_data sa8775p_data = {
.ubwc_enc_version = UBWC_4_0,
- .ubwc_dec_version = UBWC_4_3,
- .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
- UBWC_SWIZZLE_ENABLE_LVL3,
- .ubwc_bank_spread = true,
- /* TODO: highest_bank_bit = 15 for LP_DDR4 */
- .highest_bank_bit = 16,
- .macrotile_mode = true,
+ .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2,
+ .highest_bank_bit = 13,
};
-static const struct qcom_ubwc_cfg_data sm8750_data = {
+static const struct qcom_ubwc_cfg_data glymur_data = {
.ubwc_enc_version = UBWC_5_0,
- .ubwc_dec_version = UBWC_5_0,
- .ubwc_swizzle = 6,
- .ubwc_bank_spread = true,
+ .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2 |
+ UBWC_FLAG_DISABLE_SWIZZLE_LVL3,
/* TODO: highest_bank_bit = 15 for LP_DDR4 */
.highest_bank_bit = 16,
- .macrotile_mode = true,
};
-static const struct qcom_ubwc_cfg_data glymur_data = {
- .ubwc_enc_version = UBWC_5_0,
- .ubwc_dec_version = UBWC_5_0,
- .ubwc_swizzle = 0,
- .ubwc_bank_spread = true,
- /* TODO: highest_bank_bit = 15 for LP_DDR4 */
- .highest_bank_bit = 16,
- .macrotile_mode = true,
+static const struct qcom_ubwc_cfg_data milos_data = {
+ .ubwc_enc_version = UBWC_4_0,
+ .flags = UBWC_SWIZZLE_ENABLE_LVL2 |
+ UBWC_SWIZZLE_ENABLE_LVL3,
+ /* TODO: highest_bank_bit = 14 for LP_DDR4 */
+ .highest_bank_bit = 15,
};
static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = {
{ .compatible = "qcom,apq8016", .data = &no_ubwc_data },
{ .compatible = "qcom,apq8026", .data = &no_ubwc_data },
{ .compatible = "qcom,apq8074", .data = &no_ubwc_data },
- { .compatible = "qcom,apq8096", .data = &msm8998_data },
- { .compatible = "qcom,eliza", .data = &eliza_data, },
- { .compatible = "qcom,kaanapali", .data = &kaanapali_data, },
+ { .compatible = "qcom,apq8096", .data = &ubwc_1_0_hbb15 },
+ { .compatible = "qcom,eliza", .data = &ubwc_5_0_hbb15 },
{ .compatible = "qcom,glymur", .data = &glymur_data},
+ { .compatible = "qcom,kaanapali", .data = &ubwc_6_0_hbb16 },
{ .compatible = "qcom,mahua", .data = &glymur_data },
+ { .compatible = "qcom,milos", .data = &milos_data },
{ .compatible = "qcom,msm8226", .data = &no_ubwc_data },
{ .compatible = "qcom,msm8916", .data = &no_ubwc_data },
{ .compatible = "qcom,msm8917", .data = &no_ubwc_data },
- { .compatible = "qcom,msm8937", .data = &msm8937_data },
+ { .compatible = "qcom,msm8937", .data = &ubwc_1_0_hbb14 },
{ .compatible = "qcom,msm8929", .data = &no_ubwc_data },
{ .compatible = "qcom,msm8939", .data = &no_ubwc_data },
- { .compatible = "qcom,msm8953", .data = &msm8937_data },
+ { .compatible = "qcom,msm8953", .data = &ubwc_1_0_hbb14 },
{ .compatible = "qcom,msm8956", .data = &no_ubwc_data },
{ .compatible = "qcom,msm8974", .data = &no_ubwc_data },
{ .compatible = "qcom,msm8976", .data = &no_ubwc_data },
- { .compatible = "qcom,msm8996", .data = &msm8998_data },
- { .compatible = "qcom,msm8998", .data = &msm8998_data },
- { .compatible = "qcom,qcm2290", .data = &qcm2290_data, },
- { .compatible = "qcom,qcm6490", .data = &sc7280_data, },
- { .compatible = "qcom,qcs8300", .data = &sc8280xp_data, },
- { .compatible = "qcom,sa8155p", .data = &sm8150_data, },
- { .compatible = "qcom,sa8540p", .data = &sc8280xp_data, },
+ { .compatible = "qcom,msm8996", .data = &ubwc_1_0_hbb15 },
+ { .compatible = "qcom,msm8998", .data = &ubwc_1_0_hbb15 },
+ { .compatible = "qcom,qcm2290", .data = &ubwc_0_0_hbb15, },
+ { .compatible = "qcom,qcm6490", .data = &ubwc_3_1_hbb14, },
+ { .compatible = "qcom,qcs8300", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sa8155p", .data = &ubwc_3_0_hbb15, },
+ { .compatible = "qcom,sa8540p", .data = &ubwc_4_0_hbb16, },
{ .compatible = "qcom,sa8775p", .data = &sa8775p_data, },
- { .compatible = "qcom,sar2130p", .data = &sar2130p_data },
- { .compatible = "qcom,sc7180", .data = &sc7180_data },
- { .compatible = "qcom,sc7280", .data = &sc7280_data, },
- { .compatible = "qcom,sc8180x", .data = &sc8180x_data, },
- { .compatible = "qcom,sc8280xp", .data = &sc8280xp_data, },
- { .compatible = "qcom,sda660", .data = &msm8937_data },
- { .compatible = "qcom,sdm450", .data = &msm8937_data },
- { .compatible = "qcom,sdm630", .data = &msm8937_data },
- { .compatible = "qcom,sdm632", .data = &msm8937_data },
- { .compatible = "qcom,sdm636", .data = &msm8937_data },
- { .compatible = "qcom,sdm660", .data = &msm8937_data },
- { .compatible = "qcom,sdm670", .data = &sdm670_data, },
- { .compatible = "qcom,sdm845", .data = &sdm845_data, },
- { .compatible = "qcom,sm4250", .data = &sm6115_data, },
- { .compatible = "qcom,sm6115", .data = &sm6115_data, },
- { .compatible = "qcom,sm6125", .data = &sm6125_data, },
- { .compatible = "qcom,sm6150", .data = &sm6150_data, },
- { .compatible = "qcom,sm6350", .data = &sm6350_data, },
- { .compatible = "qcom,sm6375", .data = &sm6350_data, },
- { .compatible = "qcom,sm7125", .data = &sc7180_data },
- { .compatible = "qcom,sm7150", .data = &sm7150_data, },
- { .compatible = "qcom,sm7225", .data = &sm6350_data, },
- { .compatible = "qcom,sm7325", .data = &sc7280_data, },
- { .compatible = "qcom,sm8150", .data = &sm8150_data, },
- { .compatible = "qcom,sm8250", .data = &sm8250_data, },
- { .compatible = "qcom,sm8350", .data = &sm8350_data, },
- { .compatible = "qcom,sm8450", .data = &sm8350_data, },
- { .compatible = "qcom,sm8550", .data = &sm8550_data, },
- { .compatible = "qcom,sm8650", .data = &sm8550_data, },
- { .compatible = "qcom,sm8750", .data = &sm8750_data, },
- { .compatible = "qcom,x1e80100", .data = &sm8550_data, },
- { .compatible = "qcom,x1p42100", .data = &sm8550_data, },
+ { .compatible = "qcom,sar2130p", .data = &ubwc_3_1_hbb13 },
+ { .compatible = "qcom,sc7180", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sc7280", .data = &ubwc_3_1_hbb14, },
+ { .compatible = "qcom,sc8180x", .data = &ubwc_3_1_hbb16, },
+ { .compatible = "qcom,sc8280xp", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sda660", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm450", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm630", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm632", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm636", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm660", .data = &ubwc_1_0_hbb14 },
+ { .compatible = "qcom,sdm670", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sdm845", .data = &ubwc_2_0_hbb15, },
+ { .compatible = "qcom,shikra", .data = &ubwc_0_0_hbb15, },
+ { .compatible = "qcom,sm4250", .data = &ubwc_1_0_hbb14, },
+ { .compatible = "qcom,sm6115", .data = &ubwc_1_0_hbb14, },
+ { .compatible = "qcom,sm6125", .data = &ubwc_1_0_hbb14, },
+ { .compatible = "qcom,sm6150", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm6350", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm6375", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm7125", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm7150", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm7225", .data = &ubwc_2_0_hbb14, },
+ { .compatible = "qcom,sm7325", .data = &ubwc_3_1_hbb14, },
+ { .compatible = "qcom,sm8150", .data = &ubwc_3_0_hbb15, },
+ { .compatible = "qcom,sm8250", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sm8350", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sm8450", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sm8550", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sm8650", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,sm8750", .data = &ubwc_5_0_hbb16, },
+ { .compatible = "qcom,x1e80100", .data = &ubwc_4_0_hbb16, },
+ { .compatible = "qcom,x1p42100", .data = &ubwc_4_0_hbb16, },
{ }
};
+static struct qcom_ubwc_cfg_data *cfg;
+static DEFINE_MUTEX(cfg_mutex);
+/**
+ * qcom_ubwc_config_get_data() - Retrieve UBWC data for the platform.
+ *
+ * Return: Pointer to valid struct qcom_ubwc_cfg_data on success, negative
+ * errno on failure. Note that this may return EPROBE_DEFER.
+ */
const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void)
{
const struct qcom_ubwc_cfg_data *data;
+ int hbb;
+
+ guard(mutex)(&cfg_mutex);
+ if (cfg)
+ return cfg;
+
+ if (!qcom_smem_is_available())
+ return ERR_PTR(-EPROBE_DEFER);
data = of_machine_get_match_data(qcom_ubwc_configs);
if (!data) {
@@ -310,9 +195,30 @@ const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void)
return ERR_PTR(-EINVAL);
}
- return data;
+ hbb = qcom_smem_dram_get_hbb();
+ if (hbb == -ENODATA) {
+ /* Lack of HBB data is OK - it was only introduced later */
+ return data;
+ } else if (hbb < 0) {
+ pr_err("Couldn't get HBB data from SMEM: %d\n", hbb);
+ return ERR_PTR(hbb);
+ }
+
+ cfg = kmemdup(data, sizeof(*data), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ cfg->highest_bank_bit = hbb;
+
+ return cfg;
}
EXPORT_SYMBOL_GPL(qcom_ubwc_config_get_data);
+static void __exit ubwc_config_exit(void)
+{
+ kfree(cfg);
+}
+module_exit(ubwc_config_exit);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("UBWC config database for QTI SoCs");
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index ffb31a049d4a..942e11feba65 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -221,8 +221,10 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
left = fw->size;
req = kzalloc_flex(*req, fragment, NV_FRAGMENT_SIZE);
- if (!req)
- return -ENOMEM;
+ if (!req) {
+ ret = -ENOMEM;
+ goto release_fw;
+ }
req->frag_size = NV_FRAGMENT_SIZE;
req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
@@ -243,7 +245,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
if (ret < 0) {
dev_err(dev, "failed to send smd packet\n");
- goto release_fw;
+ goto release_req;
}
/* Increment for next fragment */
@@ -262,9 +264,10 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
ret = 0;
}
+release_req:
+ kfree(req);
release_fw:
release_firmware(fw);
- kfree(req);
return ret;
}