mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
More Qualcomm driver updates for v7.3
Add SMEM parsing for DDR configuration data and use its highest bank address bit to select the appropriate UBWC configuration. Enable generic PAS trusted-zone APIs for the Iris and Venus media drivers. Fix SCM probe retry state, reserved-memory cleanup, and an early IRQ-handler NULL dereference. Enable QSEECOM EFI variable access on the Asus Zenbook A16. Correct GENI firmware-size validation using the hardware CFG RAM depth and correct the PMIC GLINK Thunderbolt extradata layout. Document the Nord AOSS side channel and the IMEM minidump SRAM property. Clean up Qualcomm statistics macros and the WCNSS binding schema. -----BEGIN PGP SIGNATURE----- iQJCBAABCgAsFiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmp1/J8OHGJqb3JuQGty eW8uc2UACgkQCx85Pw2ZrcXcZQ/9G3XLVoPBw7j1dyAfdDs1x16kZBoHxexXbPLQ RgNZHrcz2voh4pXOF21nf5Pv9eC+i4rZU0PzIBJBzHiW/0C+QMegOzx70upIgoBM V2Xsw/eqhfcPMl0xz/6LCYijGe1UXcdl1qD+blbe4qql+BFsZdG5skmqpEA2M6ZH DiD9II5hW/XNAZOFRkV64nV+oaX3nlU9HyusPIebL5YV8ZHjBis/QQpbObLHNUQy kL7UACS86U2PoR+womdoBcESArrEEpJzdezWwxjAS8nsOFoPUjzRaYxdlODm4rad xw+fB/loedU8yLfZpcE0GdEE6qwrwt4tda3X3ZeSfOuYqq+XiRA33CeRwCDb+XNK DlOpA3bRNTY8d3LZ+Worg329kRPB86fmvYTxsvurJ7/LTVSl1ZHotL0rdFOcL8BD sR3FIRXSPt/RUE0QY7Xq38BahsgWAjKt2TFNrtq2PJdQES2kYLXpx8+xAzFWeg// TxkM8UtA5dmt19svsAtELHyTGm8lWFBtbZSyHDbdGhDH6Wvf0PHNcsqY5lYIvwn+ 5+bpT00Os2KdlgaBvWzUxM+FoWJ1QH9hYjEJZRFdcWmtbnNRABBLcyyGgtAOffot mVjRrrRkgSBn+94eZTmqC25RPwZbkd1hog9ym5JUa2RLYfgsKDHW6rb1p/+Qp601 diAdn9s= =hi1p -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmp5hSsACgkQmmx57+YA GNk9Lw//d58iG3MEb2ttm1MTuUv1S5f3kaleALzaGJh7mXk7MrtdYd+nVEKsXJ4m tmBQW2TmrjUdj36YkIhkyuiX0iE2Y6Gje+pm1v7axbqWGJ3Kr48Ytrc/QrlCS6Ji nHz4aZx7Lm6HYiGkl9QVajkP3w/FDnlk+awLc0kN/0BqzEkyPKrJYTePz7FHnfhy kRmpzMBZY05MG3MxxJU57bJHWntmKL4rCSpTUdVMP28lb8lIRLbrK7IEIoCR+Css bciH++o8pBFrJv3ryexMlUmR5Ua38BjuoDXeK17PwqoB2My8CJUJAf8+dKVWGJ9h efpSGJKfUjpDTUJKqKESx11jb900DPpFg773JwtJCRTP+7TZU1iv/6KxyfNgz6Qx LGNUEehZ6nYRUo91v5FX4jBaafEHqpl/PLUQIPqvaCcYTQ1pw+TZKBhA/EZJucuv RrE9fLt1ivn7+YaJSrEavzsPzMiXHs+Y61zbb1OEIGAg07uu9GUTjhaI+DfUIDNf VT5v8Y4TopL8NEHsSekaJ4Zu4gGe/gOreZWlxDBtbtMWB7T4KmgRPJhAmrp40bQq B6LcmsDw80YCa46MRbF9PnvM7YEOdZox4Riw8V5IcdHd2IjwfMGPD75TY56mMlIC u0LBNKuaOtG+Y9m7TeJ/W8kElAib71dh4wZb5xmqrZcVLmRgtxo= =TIvW -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-7.3-2' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers More Qualcomm driver updates for v7.3 Add SMEM parsing for DDR configuration data and use its highest bank address bit to select the appropriate UBWC configuration. Enable generic PAS trusted-zone APIs for the Iris and Venus media drivers. Fix SCM probe retry state, reserved-memory cleanup, and an early IRQ-handler NULL dereference. Enable QSEECOM EFI variable access on the Asus Zenbook A16. Correct GENI firmware-size validation using the hardware CFG RAM depth and correct the PMIC GLINK Thunderbolt extradata layout. Document the Nord AOSS side channel and the IMEM minidump SRAM property. Clean up Qualcomm statistics macros and the WCNSS binding schema. * tag 'qcom-drivers-for-7.3-2' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: media: qcom: Switch to generic PAS TZ APIs dt-bindings: soc: qcom,aoss-qmp: Document Nord AOSS side channel dt-bindings: sram: qcom,imem: Add minidump-sram pattern property soc: qcom: qcom_stats: Replace CLIENT_VOTES_OFFSET macro with sizeof() soc: qcom: qcom_stats: Remove unused macro definitions soc: qcom: ubwc: Get HBB from SMEM soc: qcom: smem: Expose DDR data from SMEM soc: qcom: smem: Use 'unsigned int' instead of 'unsigned' firmware: qcom: scm: Fix tzmem state on probe retry firmware: qcom: scm: Fix reserved memory cleanup on probe failure firmware: qcom: scm: Fix NULL dereference in IRQ handler before __scm is published firmware: qcom: scm: Allow QSEECOM on Asus Zenbook A16 (UX3607OA) soc: qcom: geni-se: Use HW PROG_RAM_DEPTH to validate firmware size soc: qcom: pmic_glink_altmode: Define the TBT extradata properly dt-bindings: soc: qcom,wcnss: Drop redundant $ref of firmware-name property Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
93d524fb91
|
|
@ -31,6 +31,7 @@ properties:
|
|||
- qcom,kaanapali-aoss-qmp
|
||||
- qcom,maili-aoss-qmp
|
||||
- qcom,milos-aoss-qmp
|
||||
- qcom,nord-aoss-qmp
|
||||
- qcom,qcs615-aoss-qmp
|
||||
- qcom,qcs8300-aoss-qmp
|
||||
- qcom,qdu1000-aoss-qmp
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ properties:
|
|||
const: qcom,wcnss
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
default: wlan/prima/WCNSS_qcom_wlan_nv.bin
|
||||
maxItems: 1
|
||||
default: [ wlan/prima/WCNSS_qcom_wlan_nv.bin ]
|
||||
description:
|
||||
Relative firmware image path for the WLAN NV blob.
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,22 @@ properties:
|
|||
$ref: /schemas/power/reset/syscon-reboot-mode.yaml#
|
||||
|
||||
patternProperties:
|
||||
"^minidump-sram@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
A word in always-on SRAM shared between the kernel and boot firmware.
|
||||
Before DDR is initialised on the warm reset following a crash, firmware
|
||||
reads this word to decide where to deliver the minidump (USB or storage).
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
"^modem-tables@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_
|
|||
smc_call_ctx = res->a2;
|
||||
|
||||
trace_scm_waitq_sleep(wq_ctx, smc_call_ctx);
|
||||
ret = qcom_scm_wait_for_wq_completion(wq_ctx);
|
||||
ret = qcom_scm_wait_for_wq_completion(dev, wq_ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -2304,6 +2304,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
|
|||
{ .compatible = "asus,vivobook-s15-x1p4" },
|
||||
{ .compatible = "asus,zenbook-a14-ux3407qa" },
|
||||
{ .compatible = "asus,zenbook-a14-ux3407ra" },
|
||||
{ .compatible = "asus,zenbook-a16-ux3607oa" },
|
||||
{ .compatible = "dell,inspiron-14-plus-7441" },
|
||||
{ .compatible = "dell,latitude-7455" },
|
||||
{ .compatible = "dell,xps13-9345" },
|
||||
|
|
@ -2644,23 +2645,20 @@ static int qcom_scm_get_waitq_irq(struct qcom_scm *scm)
|
|||
return irq_create_fwspec_mapping(&fwspec);
|
||||
}
|
||||
|
||||
static struct completion *qcom_scm_get_completion(u32 wq_ctx)
|
||||
static struct completion *qcom_scm_get_completion(struct qcom_scm *scm, u32 wq_ctx)
|
||||
{
|
||||
struct completion *wq;
|
||||
|
||||
if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt))
|
||||
if (WARN_ON_ONCE(wq_ctx >= scm->wq_cnt))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
wq = &__scm->waitq_comps[wq_ctx];
|
||||
|
||||
return wq;
|
||||
return &scm->waitq_comps[wq_ctx];
|
||||
}
|
||||
|
||||
int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
|
||||
int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx)
|
||||
{
|
||||
struct qcom_scm *scm = dev_get_drvdata(dev);
|
||||
struct completion *wq;
|
||||
|
||||
wq = qcom_scm_get_completion(wq_ctx);
|
||||
wq = qcom_scm_get_completion(scm, wq_ctx);
|
||||
if (IS_ERR(wq))
|
||||
return PTR_ERR(wq);
|
||||
|
||||
|
|
@ -2669,11 +2667,11 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
|
||||
static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx)
|
||||
{
|
||||
struct completion *wq;
|
||||
|
||||
wq = qcom_scm_get_completion(wq_ctx);
|
||||
wq = qcom_scm_get_completion(scm, wq_ctx);
|
||||
if (IS_ERR(wq))
|
||||
return PTR_ERR(wq);
|
||||
|
||||
|
|
@ -2700,7 +2698,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = qcom_scm_waitq_wakeup(wq_ctx);
|
||||
ret = qcom_scm_waitq_wakeup(scm, wq_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
} while (more_pending);
|
||||
|
|
@ -2804,6 +2802,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
scm->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, scm);
|
||||
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
|
|
@ -2851,9 +2850,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
"Failed to setup the reserved memory region for TZ mem\n");
|
||||
|
||||
ret = qcom_tzmem_enable(scm->dev);
|
||||
if (ret)
|
||||
return dev_err_probe(scm->dev, ret,
|
||||
"Failed to enable the TrustZone memory allocator\n");
|
||||
if (ret) {
|
||||
ret = dev_err_probe(scm->dev, ret,
|
||||
"Failed to enable the TrustZone memory allocator\n");
|
||||
goto err_rmem;
|
||||
}
|
||||
|
||||
memset(&pool_config, 0, sizeof(pool_config));
|
||||
pool_config.initial_size = 0;
|
||||
|
|
@ -2861,9 +2862,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
pool_config.max_size = SZ_256K;
|
||||
|
||||
scm->mempool = devm_qcom_tzmem_pool_new(scm->dev, &pool_config);
|
||||
if (IS_ERR(scm->mempool))
|
||||
return dev_err_probe(scm->dev, PTR_ERR(scm->mempool),
|
||||
"Failed to create the SCM memory pool\n");
|
||||
if (IS_ERR(scm->mempool)) {
|
||||
ret = dev_err_probe(scm->dev, PTR_ERR(scm->mempool),
|
||||
"Failed to create the SCM memory pool\n");
|
||||
goto err_rmem;
|
||||
}
|
||||
|
||||
ret = qcom_scm_query_waitq_count(scm);
|
||||
scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret;
|
||||
|
|
@ -2939,6 +2942,10 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
qcom_scm_gunyah_wdt_init(scm);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rmem:
|
||||
of_reserved_mem_device_release(scm->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_scm_shutdown(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ struct qcom_scm_res {
|
|||
u64 result[MAX_QCOM_SCM_RETS];
|
||||
};
|
||||
|
||||
int qcom_scm_wait_for_wq_completion(u32 wq_ctx);
|
||||
int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx);
|
||||
int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending);
|
||||
|
||||
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/once.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
@ -507,14 +508,18 @@ phys_addr_t qcom_tzmem_to_phys(void *vaddr)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_tzmem_to_phys);
|
||||
|
||||
static void qcom_tzmem_do_init(int *result)
|
||||
{
|
||||
*result = qcom_tzmem_init();
|
||||
}
|
||||
|
||||
int qcom_tzmem_enable(struct device *dev)
|
||||
{
|
||||
if (qcom_tzmem_dev)
|
||||
return -EBUSY;
|
||||
static int result;
|
||||
|
||||
qcom_tzmem_dev = dev;
|
||||
|
||||
return qcom_tzmem_init();
|
||||
DO_ONCE(qcom_tzmem_do_init, &result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_tzmem_enable);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
config VIDEO_QCOM_IRIS
|
||||
tristate "Qualcomm iris V4L2 decoder driver"
|
||||
depends on VIDEO_DEV
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select V4L2_MEM2MEM_DEV
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_SCM
|
||||
select QCOM_UBWC_CONFIG
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
help
|
||||
This is a V4L2 driver for Qualcomm iris video accelerator
|
||||
hardware. It accelerates decoding operations on various
|
||||
Qualcomm SoCs.
|
||||
To compile this driver as a module choose m here.
|
||||
tristate "Qualcomm iris V4L2 decoder driver"
|
||||
depends on VIDEO_DEV
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select V4L2_MEM2MEM_DEV
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_SCM
|
||||
select QCOM_PAS
|
||||
select QCOM_UBWC_CONFIG
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
help
|
||||
This is a V4L2 driver for Qualcomm iris video accelerator
|
||||
hardware. It accelerates decoding operations on various
|
||||
Qualcomm SoCs.
|
||||
To compile this driver as a module choose m here.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/firmware/qcom/qcom_pas.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
|
@ -80,7 +81,7 @@ int iris_fw_load(struct iris_core *core)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID);
|
||||
ret = qcom_pas_auth_and_reset(IRIS_PAS_ID);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "auth and reset failed: %d\n", ret);
|
||||
return ret;
|
||||
|
|
@ -94,7 +95,7 @@ int iris_fw_load(struct iris_core *core)
|
|||
cp_config->cp_nonpixel_size);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
|
||||
qcom_scm_pas_shutdown(IRIS_PAS_ID);
|
||||
qcom_pas_shutdown(IRIS_PAS_ID);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -104,10 +105,10 @@ int iris_fw_load(struct iris_core *core)
|
|||
|
||||
int iris_fw_unload(struct iris_core *core)
|
||||
{
|
||||
return qcom_scm_pas_shutdown(IRIS_PAS_ID);
|
||||
return qcom_pas_shutdown(IRIS_PAS_ID);
|
||||
}
|
||||
|
||||
int iris_set_hw_state(struct iris_core *core, bool resume)
|
||||
{
|
||||
return qcom_scm_set_remote_state(resume, 0);
|
||||
return qcom_pas_set_remote_state(resume, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ config VIDEO_QCOM_VENUS
|
|||
select OF_DYNAMIC if ARCH_QCOM
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_SCM
|
||||
select QCOM_PAS
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select V4L2_MEM2MEM_DEV
|
||||
help
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/firmware/qcom/qcom_pas.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/soc/qcom/mdt_loader.h>
|
||||
|
|
@ -58,7 +59,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume)
|
|||
int ret;
|
||||
|
||||
if (core->use_tz) {
|
||||
ret = qcom_scm_set_remote_state(resume, 0);
|
||||
ret = qcom_pas_set_remote_state(resume, 0);
|
||||
if (resume && ret == -EINVAL)
|
||||
ret = 0;
|
||||
return ret;
|
||||
|
|
@ -218,7 +219,7 @@ int venus_boot(struct venus_core *core)
|
|||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
|
||||
(core->use_tz && !qcom_scm_is_available()))
|
||||
(core->use_tz && !qcom_pas_is_available()))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
|
||||
|
|
@ -236,7 +237,7 @@ int venus_boot(struct venus_core *core)
|
|||
core->fw.mem_phys = mem_phys;
|
||||
|
||||
if (core->use_tz)
|
||||
ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
|
||||
ret = qcom_pas_auth_and_reset(VENUS_PAS_ID);
|
||||
else
|
||||
ret = venus_boot_no_tz(core, mem_phys, mem_size);
|
||||
|
||||
|
|
@ -259,7 +260,7 @@ int venus_boot(struct venus_core *core)
|
|||
res->cp_nonpixel_start,
|
||||
res->cp_nonpixel_size);
|
||||
if (ret) {
|
||||
qcom_scm_pas_shutdown(VENUS_PAS_ID);
|
||||
qcom_pas_shutdown(VENUS_PAS_ID);
|
||||
dev_err(dev, "set virtual address ranges fail (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
|
|
@ -274,7 +275,7 @@ int venus_shutdown(struct venus_core *core)
|
|||
int ret;
|
||||
|
||||
if (core->use_tz)
|
||||
ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
|
||||
ret = qcom_pas_shutdown(VENUS_PAS_ID);
|
||||
else
|
||||
ret = venus_shutdown_no_tz(core);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -154,8 +154,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
|
||||
|
|
@ -1224,24 +1222,27 @@ 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;
|
||||
|
||||
|
|
@ -1300,10 +1301,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;
|
||||
}
|
||||
|
||||
|
|
@ -1427,7 +1429,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -140,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -267,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
|
||||
*/
|
||||
|
|
@ -281,7 +285,9 @@ struct qcom_smem {
|
|||
struct smem_partition global_partition;
|
||||
struct xarray partitions;
|
||||
|
||||
unsigned num_regions;
|
||||
struct dentry *debugfs_dir;
|
||||
|
||||
unsigned int num_regions;
|
||||
struct smem_region regions[] __counted_by(num_regions);
|
||||
};
|
||||
|
||||
|
|
@ -399,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;
|
||||
|
|
@ -460,7 +466,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
|
|||
}
|
||||
|
||||
static int qcom_smem_alloc_global(struct qcom_smem *smem,
|
||||
unsigned item,
|
||||
unsigned int item,
|
||||
size_t size)
|
||||
{
|
||||
struct smem_global_entry *entry;
|
||||
|
|
@ -503,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;
|
||||
|
|
@ -544,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;
|
||||
|
|
@ -553,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];
|
||||
|
|
@ -562,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) {
|
||||
|
|
@ -584,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;
|
||||
|
|
@ -672,6 +677,30 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
|
|||
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
|
||||
|
|
@ -683,28 +712,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
|
|||
*
|
||||
* 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);
|
||||
|
||||
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;
|
||||
return __qcom_smem_get(__smem, host, item, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_smem_get);
|
||||
|
||||
|
|
@ -717,12 +727,12 @@ 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);
|
||||
|
|
@ -1237,19 +1247,26 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
|||
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);
|
||||
|
|
|
|||
12
drivers/soc/qcom/smem.h
Normal file
12
drivers/soc/qcom/smem.h
Normal file
|
|
@ -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
|
||||
430
drivers/soc/qcom/smem_dramc.c
Normal file
430
drivers/soc/qcom/smem_dramc.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#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 */
|
||||
|
|
@ -168,9 +169,25 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = {
|
|||
{ }
|
||||
};
|
||||
|
||||
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) {
|
||||
|
|
@ -178,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");
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ struct geni_se {
|
|||
#define SE_DMA_RX_FSM_RST 0xd58
|
||||
#define SE_HW_PARAM_0 0xe24
|
||||
#define SE_HW_PARAM_1 0xe28
|
||||
#define SE_HW_PARAM_2 0xe2c
|
||||
|
||||
/* GENI_FORCE_DEFAULT_REG fields */
|
||||
#define FORCE_DEFAULT BIT(0)
|
||||
|
|
@ -291,6 +292,9 @@ struct geni_se {
|
|||
#define RX_FIFO_DEPTH_MSK GENMASK(21, 16)
|
||||
#define RX_FIFO_DEPTH_SHFT 16
|
||||
|
||||
/* SE_HW_PARAM_2 fields */
|
||||
#define PROG_RAM_DEPTH_MSK GENMASK(10, 0)
|
||||
|
||||
#define HW_VER_MAJOR_MASK GENMASK(31, 28)
|
||||
#define HW_VER_MAJOR_SHFT 28
|
||||
#define HW_VER_MINOR_MASK GENMASK(27, 16)
|
||||
|
|
|
|||
|
|
@ -17,4 +17,6 @@ int qcom_smem_get_feature_code(u32 *code);
|
|||
|
||||
int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
|
||||
|
||||
int qcom_smem_dram_get_hbb(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user