diff --git a/android/ACK_SHA b/android/ACK_SHA index 85bd64718859..2272aff17b9e 100644 --- a/android/ACK_SHA +++ b/android/ACK_SHA @@ -1,2 +1,2 @@ -c02e95e82b343fe03d03dfd6fb74ecc2077e1ee3 -android12-5.10-2024-05_r1 +7e657aa362f21c8fb472e03a22795b18f831a178 +android12-5.10-2024-08_r1 diff --git a/android/abi_gki_aarch64.xml b/android/abi_gki_aarch64.xml index 3f244f8788a6..86f118c8f763 100755 --- a/android/abi_gki_aarch64.xml +++ b/android/abi_gki_aarch64.xml @@ -78522,9 +78522,9 @@ - + - + diff --git a/arch/arm64/configs/vendor/anorak.config b/arch/arm64/configs/vendor/anorak.config index 008d477727fa..b80d891fa271 100644 --- a/arch/arm64/configs/vendor/anorak.config +++ b/arch/arm64/configs/vendor/anorak.config @@ -214,6 +214,7 @@ CONFIG_PWM_QTI_LPG=m CONFIG_QCOM_AOSS_QMP=m CONFIG_QCOM_BALANCE_ANON_FILE_RECLAIM=y CONFIG_QCOM_BAM_DMA=m +CONFIG_QCOM_BUS_PROF=m CONFIG_QCOM_BWMON=m CONFIG_QCOM_BWPROF=m # CONFIG_QCOM_CLK_APCC_MSM8996 is not set @@ -249,6 +250,7 @@ CONFIG_QCOM_KGSL_CONTEXT_DEBUG=y CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT=y CONFIG_QCOM_LAZY_MAPPING=m CONFIG_QCOM_LLCC=m +CONFIG_QCOM_LLCC_MISS=m CONFIG_QCOM_LLCC_PERFMON=m CONFIG_QCOM_LLCC_PMU=m CONFIG_QCOM_LOGBUF_VENDOR_HOOKS=m diff --git a/arch/arm64/configs/vendor/neo_le.config b/arch/arm64/configs/vendor/neo_le.config index b41fd7d82eaa..071b6b06a521 100644 --- a/arch/arm64/configs/vendor/neo_le.config +++ b/arch/arm64/configs/vendor/neo_le.config @@ -369,6 +369,7 @@ CONFIG_REGMAP_QTI_DEBUGFS=m CONFIG_REGMAP_SPMI=m CONFIG_REGULATOR_DEBUG_CONTROL=y CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_QCOM_PM8008=m CONFIG_REGULATOR_QCOM_RPMH=m CONFIG_REGULATOR_QTI_FIXED_VOLTAGE=y CONFIG_REGULATOR_RPMH=m diff --git a/drivers/clk/qcom/gcc-anorak.c b/drivers/clk/qcom/gcc-anorak.c index 29ad24fc2e8c..72e801e8460b 100644 --- a/drivers/clk/qcom/gcc-anorak.c +++ b/drivers/clk/qcom/gcc-anorak.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022, 2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -3773,6 +3773,7 @@ static struct clk_branch gcc_usb2_0_clkref_en = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_usb2_0_clkref_en", + .flags = CLK_DONT_HOLD_STATE, .ops = &clk_branch2_ops, }, }, diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index d632150a8012..f7ab766f6b5b 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -528,18 +528,15 @@ int __qcom_scm_set_dload_mode(struct device *dev, enum qcom_download_mode mode) void qcom_scm_set_download_mode(enum qcom_download_mode mode, phys_addr_t tcsr_boot_misc) { - bool avail; int ret = 0; struct device *dev = __scm ? __scm->dev : NULL; - avail = __qcom_scm_is_call_available(dev, - QCOM_SCM_SVC_BOOT, - QCOM_SCM_BOOT_SET_DLOAD_MODE); - if (avail) { + if (tcsr_boot_misc || (__scm && __scm->dload_mode_addr)) { + ret = qcom_scm_io_writel(tcsr_boot_misc ? : __scm->dload_mode_addr, mode); + } else if (__qcom_scm_is_call_available(dev, + QCOM_SCM_SVC_BOOT, + QCOM_SCM_BOOT_SET_DLOAD_MODE)) { ret = __qcom_scm_set_dload_mode(dev, mode); - } else if (tcsr_boot_misc || (__scm && __scm->dload_mode_addr)) { - ret = qcom_scm_io_writel( - tcsr_boot_misc ? : __scm->dload_mode_addr, mode); } else { dev_err(dev, "No available mechanism for setting download mode\n"); @@ -806,6 +803,34 @@ int qcom_scm_get_sec_dump_state(u32 *dump_state) } EXPORT_SYMBOL(qcom_scm_get_sec_dump_state); +int __qcom_scm_get_llcc_missrate(struct device *dev, phys_addr_t in_buf, + size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MISSRATE, + .cmd = QCOM_SCM_GET_LLCC_MISSRATE_STATS_ID, + .owner = ARM_SMCCC_OWNER_SIP, + .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL), + }; + + desc.args[0] = in_buf; + desc.args[1] = in_buf_size; + desc.args[2] = out_buf; + desc.args[3] = out_buf_size; + ret = qcom_scm_call(dev, &desc, NULL); + + return ret; +} + +int qcom_scm_get_llcc_missrate(phys_addr_t in_buf, + size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size) +{ + return __qcom_scm_get_llcc_missrate(__scm ? __scm->dev : NULL, in_buf, + in_buf_size, out_buf, out_buf_size); +} +EXPORT_SYMBOL_GPL(qcom_scm_get_llcc_missrate); + int qcom_scm_assign_dump_table_region(bool is_assign, phys_addr_t addr, size_t size) { struct qcom_scm_desc desc = { diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index be0a33414813..08e60c3f1854 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -231,6 +231,9 @@ int qcom_scm_handle_wait(struct device *dev, int scm_ret, #define QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE 0x01 #define QCOM_SCM_LMH_LIMIT_DCVSH 0x10 +#define QCOM_SCM_GET_LLCC_MISSRATE_STATS_ID 0x14 +#define QCOM_SCM_SVC_MISSRATE 0x06 + extern void __qcom_scm_init(void); /* common error codes */ diff --git a/drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c b/drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c index 8a5e02e09e56..6fb34fd766ea 100644 --- a/drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c +++ b/drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c @@ -146,11 +146,6 @@ static void log_profiling_info(struct adreno_device *adreno_dev, u32 *rcvd) if (context == NULL) return; - /* protected GPU work must not be reported */ - if (!(context->flags & KGSL_CONTEXT_SECURE)) - kgsl_work_period_update(device, context->proc_priv->period, - cmd->active); - info.timestamp = cmd->ts; info.rb_id = adreno_get_level(context); info.gmu_dispatch_queue = context->gmu_dispatch_queue; @@ -163,6 +158,10 @@ static void log_profiling_info(struct adreno_device *adreno_dev, u32 *rcvd) info.active = cmd->active; info.retired_on_gmu = cmd->retired_on_gmu; + /* protected GPU work must not be reported */ + if (!(context->flags & KGSL_CONTEXT_SECURE)) + kgsl_work_period_update(device, context->proc_priv->period, info.active); + trace_adreno_cmdbatch_retired(context, &info, 0, 0, 0); log_kgsl_cmdbatch_retired_event(context->id, cmd->ts, context->priority, diff --git a/drivers/i2c/busses/i2c-msm-geni.c b/drivers/i2c/busses/i2c-msm-geni.c index c0a746f07b16..ba803e94c2d7 100644 --- a/drivers/i2c/busses/i2c-msm-geni.c +++ b/drivers/i2c/busses/i2c-msm-geni.c @@ -799,8 +799,16 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) static void gi2c_ev_cb(struct dma_chan *ch, struct msm_gpi_cb const *cb_str, void *ptr) { - struct geni_i2c_dev *gi2c = ptr; - u32 m_stat = cb_str->status; + struct geni_i2c_dev *gi2c; + u32 m_stat; + + if (!ptr || !cb_str) { + pr_err("%s: Invalid ev_cb buffer\n", __func__); + return; + } + + gi2c = (struct geni_i2c_dev *)ptr; + m_stat = cb_str->status; switch (cb_str->cb_event) { case MSM_GPI_QUP_ERROR: diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 1edfb2fffa83..dda4b8f9c331 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -324,8 +324,8 @@ static struct regmap *qcom_icc_rpmh_map(struct platform_device *pdev, static bool is_voter_disabled(char *voter) { - if ((strnstr(voter, "disp", strlen(voter)) && - (socinfo_get_part_info(PART_DISPLAY) || socinfo_get_part_info(PART_DISPLAY1))) || + if ((!strcmp(voter, "disp") && socinfo_get_part_info(PART_DISPLAY)) || + (!strcmp(voter, "disp2") && socinfo_get_part_info(PART_DISPLAY1)) || (strnstr(voter, "cam", strlen(voter)) && socinfo_get_part_info(PART_CAMERA))) return true; @@ -359,7 +359,12 @@ static int qcom_icc_init_disabled_parts(struct qcom_icc_provider *qp) if (!qn) continue; - if (strnstr(qn->name, voter_name, strlen(qn->name))) + /* + * Find the ICC node to be disabled by comparing voter_name in + * node name string, adjust the start position accordingly + */ + if (!strcmp(qn->name + (strlen(qn->name) - strlen(voter_name)), + voter_name)) qn->disabled = true; } } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index c543550f2b82..76a2948a584d 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -8509,7 +8509,9 @@ static int qseecom_release(struct inode *inode, struct file *file) static const struct file_operations qseecom_fops = { .owner = THIS_MODULE, .unlocked_ioctl = qseecom_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = compat_qseecom_ioctl, +#endif .open = qseecom_open, .release = qseecom_release }; diff --git a/drivers/power/reset/qcom-dload-mode.c b/drivers/power/reset/qcom-dload-mode.c index 46b86d92bc20..8af5318d526f 100644 --- a/drivers/power/reset/qcom-dload-mode.c +++ b/drivers/power/reset/qcom-dload-mode.c @@ -98,7 +98,9 @@ static int param_set_download_mode(const char *val, if (ret) return ret; - msm_enable_dump_mode(true); + msm_enable_dump_mode(enable_dump); + if (!enable_dump) + qcom_scm_disable_sdi(); return 0; } diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 1ab2a70cc17d..809b1fb209a6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -1171,6 +1171,7 @@ config QCOM_HUNG_TASK_ENH config QCOM_SMCINVOKE tristate "Secure QSEE Support" + depends on QCOM_SCM && QCOM_MEM_BUF help Enable SMCInvoke driver which supports capability based secure communication between QTI Secure Execution Environment (QSEE) diff --git a/drivers/soc/qcom/dcvs/Kconfig b/drivers/soc/qcom/dcvs/Kconfig index 3e541bc5e8e4..3d0dc6343685 100644 --- a/drivers/soc/qcom/dcvs/Kconfig +++ b/drivers/soc/qcom/dcvs/Kconfig @@ -76,6 +76,29 @@ config QTI_HW_MEMLAT_SCMI_CLIENT driver, and interface driver will use this handle to communicate with memlat HW. +config QCOM_LLCC_MISS + tristate "Qualcomm Technologies Inc. LLCC_MISS_STATS Driver" + depends on ARCH_QCOM && QCOM_BUS_PROF + default n + help + This enables the QCOM LLCC miss stats driver which exposes LLCC miss + stats to user space clients at modest time intervals for the intention + of profiling. + + The purpose of the driver is to enable the monitoring of LLCC misses + at regular intervals and providing the miss rate data to the user + space clients upon read. + +config QCOM_BUS_PROF + tristate "Qualcomm Technologies Inc. BUS_PROF Driver" + depends on ARCH_QCOM + default n + help + This enables the QCOM bus profiling common framework and ftraces, + which provide support for a variety of features. These features + include memory latency, LLCC miss, and LLCC occupancy, all of + which are supported on certain Qualcomm Technologies, Inc. + (QTI) chipsets. config QTI_PMU_SCMI_CLIENT tristate "Qualcomm Technologies Inc. SCMI client driver for PMU" diff --git a/drivers/soc/qcom/dcvs/Makefile b/drivers/soc/qcom/dcvs/Makefile index af0ca145d33e..8b1627f27647 100644 --- a/drivers/soc/qcom/dcvs/Makefile +++ b/drivers/soc/qcom/dcvs/Makefile @@ -7,6 +7,8 @@ qcom-dcvs-y := dcvs.o dcvs_icc.o dcvs_epss.o trace-dcvs.o obj-$(CONFIG_QCOM_MEMLAT) += memlat.o obj-$(CONFIG_QCOM_BWMON) += bwmon.o obj-$(CONFIG_QCOM_BWPROF) += bwprof.o +obj-$(CONFIG_QCOM_BUS_PROF) += trace-bus-prof.o +obj-$(CONFIG_QCOM_LLCC_MISS) += llcc_miss.o obj-$(CONFIG_QTI_PMU_SCMI_CLIENT) += pmu_scmi.o obj-$(CONFIG_QTI_C1DCVS_SCMI_CLIENT) += c1dcvs_scmi.o obj-$(CONFIG_QTI_HW_MEMLAT_SCMI_CLIENT) += memlat_scmi.o diff --git a/drivers/soc/qcom/dcvs/bus_prof.h b/drivers/soc/qcom/dcvs/bus_prof.h new file mode 100644 index 000000000000..9c9985d2acd0 --- /dev/null +++ b/drivers/soc/qcom/dcvs/bus_prof.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#ifndef _QCOM_BUS_PROF_H +#define _QCOM_BUS_PROF_H +#define MAX_CONCURRENT_MASTERS 2 +struct llcc_miss_buf { + u8 master_id; + uint16_t miss_info; + u32 rd_miss; + u32 wr_miss; + u32 all_access; +} __packed; + +#endif /* _QCOM_BUS_PROF_H */ diff --git a/drivers/soc/qcom/dcvs/llcc_miss.c b/drivers/soc/qcom/dcvs/llcc_miss.c new file mode 100644 index 000000000000..dbfdea250ce9 --- /dev/null +++ b/drivers/soc/qcom/dcvs/llcc_miss.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bus_prof.h" +#include "trace-bus-prof.h" + +#define SAMPLE_MS 10 +#define DDR_OUT_BUF_MAGIC (0xDD4) + +enum cmd { + LLCC_MISS_START_PROFILING = 1, + LLCC_MISS_GET_DATA = 2, + LLCC_MISS_STOP_PROFILING = 3, +}; + +enum error { + E_SUCCESS = 0, /* Operation successful */ + E_FAILURE = 1, /* Operation failed due to unknown err*/ + E_NULL_PARAM = 2,/* Null Parameter */ + E_INVALID_ARG = 3,/* Arg is not recognized */ + E_BAD_ADDRESS = 4,/* Ptr arg is bad address */ + E_INVALID_ARG_LEN = 5,/* Arg length is wrong */ + E_NOT_SUPPORTED = 6,/* Operation not supported */ + E_UNINITIALIZED = 7,/* Operation not permitted on platform */ + E_PARTIAL_DUMP = 8,/* Operation not permitted right now */ + E_RESERVED = 0x7FFFFFFF +}; + +enum llcc_miss_masters { + CPU = 0, + GPU, + NSP, + MAX_MASTER, +}; + +static char *master_names[MAX_MASTER] = {"CPU", "GPU", "NSP"}; + +struct llcc_miss_start_req { + u32 cmd_id; + u32 active_masters; +} __packed; + +struct llcc_miss_resp { + u32 cmd_id; + enum error status; +} __packed; + +struct llcc_miss_get_req { + u32 cmd_id; + u8 *buf_ptr; + u32 buf_size; + u32 type; /* Stop : 0, Reset : 1 */ +} __packed; + +struct llcc_miss_stop_req { + u32 cmd_id; +} __packed; + +/* cmd_id is expected to be defined first for each member of the union */ +union llcc_miss_req { + struct llcc_miss_start_req start_req; + struct llcc_miss_get_req get_req; + struct llcc_miss_stop_req stop_req; +} __packed; + +struct llcc_cmd_buf { + union llcc_miss_req llcc_miss_req; + struct llcc_miss_resp llcc_miss_resp; + u32 req_size; +} __packed; + +struct llcc_miss_data { + struct llcc_miss_buf master_buf[MAX_CONCURRENT_MASTERS]; + enum error err; + u16 magic; + u64 qtime; +} __packed; + +struct miss_sample { + u64 ts; + u16 measured_miss_rate; +} __packed; + +struct llcc_miss_dev_data { + struct work_struct work; + struct workqueue_struct *wq; + struct hrtimer hrtimer; + u16 max_samples; + u16 size_of_line; + u32 active_masters; + u32 available_masters; + struct llcc_miss_data *data; + struct mutex lock; +}; + +struct master_data { + u16 curr_idx; + u16 unread_samples; + struct miss_sample *miss_data; + char buf[PAGE_SIZE]; +}; + +static struct master_data mdata[MAX_MASTER]; +static struct dentry *llcc_miss_dir; +static struct llcc_miss_dev_data *llcc_miss; + +static ssize_t get_last_samples(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + int index = 0, ret = 0, m_idx; + int i = 0, size = 0, enable; + char *master_name; + + mutex_lock(&llcc_miss->lock); + if (!llcc_miss->active_masters) { + pr_err("No master is enabled for mem miss\n"); + goto unlock; + } + master_name = file->private_data; + for (m_idx = 0; m_idx < MAX_MASTER ; m_idx++) { + if (!strcasecmp(master_names[m_idx], master_name)) + break; + } + + enable = (llcc_miss->active_masters & BIT(m_idx)); + if (!enable) { + pr_err("%s memory miss is not enabled\n", master_names[m_idx]); + ret = -EINVAL; + goto unlock; + } + + index = (mdata[m_idx].curr_idx - mdata[m_idx].unread_samples + + llcc_miss->max_samples) % llcc_miss->max_samples; + for (i = 0; i < mdata[m_idx].unread_samples; i++) { + size += scnprintf(mdata[m_idx].buf + size, PAGE_SIZE - size, + "%llx\t%x\n", mdata[m_idx].miss_data[index].ts, + mdata[m_idx].miss_data[index].measured_miss_rate); + index = (index + 1) % llcc_miss->max_samples; + } + + mdata[m_idx].unread_samples = 0; + ret = simple_read_from_buffer(user_buf, count, ppos, mdata[m_idx].buf, size); +unlock: + mutex_unlock(&llcc_miss->lock); + + return ret; +} + +static int send_llcc_miss_profiling_command(const void *req) +{ + int ret = 0; + u32 qseos_cmd_id = 0; + struct llcc_miss_resp *rsp = NULL; + size_t req_size = 0, rsp_size = 0; + struct qtee_shm shm = {0}; + + if (!req) + return -EINVAL; + rsp = &((struct llcc_cmd_buf *)req)->llcc_miss_resp; + rsp_size = sizeof(struct llcc_miss_resp); + req_size = ((struct llcc_cmd_buf *)req)->req_size; + qseos_cmd_id = *(u32 *)req; + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(req_size + rsp_size), &shm); + if (ret) { + pr_err("qtee_shmbridge_allocate_shm failed, ret :%d\n", ret); + return -ENOMEM; + } + + memcpy(shm.vaddr, req, req_size); + qtee_shmbridge_flush_shm_buf(&shm); + switch (qseos_cmd_id) { + case LLCC_MISS_START_PROFILING: + case LLCC_MISS_GET_DATA: + case LLCC_MISS_STOP_PROFILING: + /* Send the command to TZ */ + ret = qcom_scm_get_llcc_missrate(shm.paddr, req_size, + shm.paddr + req_size, rsp_size); + break; + default: + pr_err("cmd_id %d is not supported.\n", + qseos_cmd_id); + ret = -EINVAL; + } + + qtee_shmbridge_inv_shm_buf(&shm); + memcpy(rsp, (char *)shm.vaddr + req_size, rsp_size); + qtee_shmbridge_free_shm(&shm); + /* Verify cmd id and Check that request succeeded. */ + if (rsp->status != 0 || + qseos_cmd_id != rsp->cmd_id) { + ret = -1; + pr_err("Status: %d,Cmd: %d\n", + rsp->status, + rsp->cmd_id); + } + + return ret; +} + +static int start_memory_miss_stats(void) +{ + int ret = 0; + struct llcc_cmd_buf *llcc_cmd_buf = NULL; + + llcc_cmd_buf = kzalloc(sizeof(*llcc_cmd_buf), GFP_KERNEL); + if (!llcc_cmd_buf) + return -ENOMEM; + llcc_cmd_buf->llcc_miss_req.start_req.cmd_id = LLCC_MISS_START_PROFILING; + llcc_cmd_buf->llcc_miss_req.start_req.active_masters = llcc_miss->active_masters; + llcc_cmd_buf->req_size = sizeof(struct llcc_miss_start_req); + ret = send_llcc_miss_profiling_command(llcc_cmd_buf); + if (ret) { + pr_err("Error in %s, ret = %d\n", __func__, ret); + goto out; + } + + if (!hrtimer_active(&llcc_miss->hrtimer)) + hrtimer_start(&llcc_miss->hrtimer, + ms_to_ktime(SAMPLE_MS), HRTIMER_MODE_REL_PINNED); +out: + kfree(llcc_cmd_buf); + + return ret; +} + +static int stop_memory_miss_stats(void) +{ + int ret; + struct llcc_cmd_buf *llcc_cmd_buf = NULL; + + hrtimer_cancel(&llcc_miss->hrtimer); + cancel_work_sync(&llcc_miss->work); + llcc_cmd_buf = kzalloc(sizeof(*llcc_cmd_buf), GFP_KERNEL); + if (!llcc_cmd_buf) + return -ENOMEM; + + llcc_cmd_buf->llcc_miss_req.stop_req.cmd_id = LLCC_MISS_STOP_PROFILING; + llcc_cmd_buf->req_size = sizeof(struct llcc_miss_stop_req); + ret = send_llcc_miss_profiling_command(llcc_cmd_buf); + if (ret) + pr_err("Error in %s, ret = %d\n", __func__, ret); + + kfree(llcc_cmd_buf); + + return 0; +} + +static int set_mon_enabled(void *data, u64 val) +{ + char *master_name = data; + int i, ret = 0; + u32 count, enable = val ? 1 : 0; + + mutex_lock(&llcc_miss->lock); + for (i = 0; i < MAX_MASTER; i++) { + if (!strcasecmp(master_names[i], master_name)) + break; + } + if (enable == (llcc_miss->active_masters & BIT(i))) + goto unlock; + count = hweight32(llcc_miss->active_masters); + if (count >= MAX_CONCURRENT_MASTERS && enable) { + pr_err("Max masters already enabled\n"); + ret = -EINVAL; + goto unlock; + } + mutex_unlock(&llcc_miss->lock); + if (count) + stop_memory_miss_stats(); + mutex_lock(&llcc_miss->lock); + llcc_miss->active_masters = (llcc_miss->active_masters ^ BIT(i)); + if (llcc_miss->active_masters) + start_memory_miss_stats(); + +unlock: + mutex_unlock(&llcc_miss->lock); + + return ret; +} + +static int get_mon_enabled(void *data, u64 *val) +{ + char *master_name = data; + int i; + + mutex_lock(&llcc_miss->lock); + for (i = 0; i < MAX_MASTER; i++) { + if (!strcasecmp(master_names[i], master_name)) + break; + } + + if (llcc_miss->active_masters & BIT(i)) + *val = 1; + else + *val = 0; + mutex_unlock(&llcc_miss->lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(set_mon_enabled_ops, get_mon_enabled, set_mon_enabled, "%llu\n"); + +static const struct file_operations show_last_samples_ops = { + .read = get_last_samples, + .open = simple_open, + .llseek = default_llseek, +}; + +static void llcc_miss_update_work(struct work_struct *work) +{ + int ret = 0, i, m; + u16 magic; + struct llcc_cmd_buf *llcc_cmd_buf; + struct qtee_shm buf_shm = {0}; + const int bufsize = sizeof(struct llcc_miss_data); + + llcc_cmd_buf = kzalloc(sizeof(*llcc_cmd_buf), GFP_KERNEL); + if (!llcc_cmd_buf) + return; + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(bufsize), &buf_shm); + if (ret) { + pr_err("shmbridge alloc buf failed\n"); + return; + } + + llcc_cmd_buf->llcc_miss_req.get_req.cmd_id = LLCC_MISS_GET_DATA; + llcc_cmd_buf->llcc_miss_req.get_req.buf_ptr = (u8 *)buf_shm.paddr; + llcc_cmd_buf->llcc_miss_req.get_req.buf_size = bufsize; + llcc_cmd_buf->llcc_miss_req.get_req.type = 1; + llcc_cmd_buf->req_size = sizeof(struct llcc_miss_get_req); + qtee_shmbridge_flush_shm_buf(&buf_shm); + ret = send_llcc_miss_profiling_command(llcc_cmd_buf); + if (ret) { + pr_err("send_llcc_miss_profiling_command failed\n"); + goto err; + } + + qtee_shmbridge_inv_shm_buf(&buf_shm); + memcpy(llcc_miss->data, (char *)buf_shm.vaddr, sizeof(*llcc_miss->data)); + magic = llcc_miss->data->magic; + if (magic != DDR_OUT_BUF_MAGIC) { + pr_err("Expected magic value is %x but got %x\n", DDR_OUT_BUF_MAGIC, magic); + goto err; + } + + mutex_lock(&llcc_miss->lock); + for (i = 0; i < MAX_CONCURRENT_MASTERS; i++) { + m = llcc_miss->data->master_buf[i].master_id; + if (m >= MAX_MASTER) + continue; + mdata[m].miss_data[mdata[m].curr_idx].ts = llcc_miss->data->qtime; + mdata[m].miss_data[mdata[m].curr_idx].measured_miss_rate = + llcc_miss->data->master_buf[i].miss_info; + mdata[m].unread_samples = min(++mdata[m].unread_samples, llcc_miss->max_samples); + mdata[m].curr_idx = (mdata[m].curr_idx + 1) % llcc_miss->max_samples; + } + + trace_memory_miss_last_sample(llcc_miss->data->qtime, + &llcc_miss->data->master_buf[0], + &llcc_miss->data->master_buf[1]); + + mutex_unlock(&llcc_miss->lock); +err: + qtee_shmbridge_free_shm(&buf_shm); + kfree(llcc_cmd_buf); +} + +static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer) +{ + ktime_t now = ktime_get(); + + queue_work(llcc_miss->wq, &llcc_miss->work); + hrtimer_forward(timer, now, ms_to_ktime(SAMPLE_MS)); + + return HRTIMER_RESTART; +} + +static int llcc_miss_create_fs_entries(void) +{ + int i; + struct dentry *ret, *master_dir; + + llcc_miss_dir = debugfs_create_dir("llcc_miss", 0); + if (IS_ERR(llcc_miss_dir)) { + pr_err("Debugfs directory creation failed for llcc_miss\n"); + return PTR_ERR(llcc_miss_dir); + } + + for (i = 0; i < MAX_MASTER; i++) { + master_dir = debugfs_create_dir(master_names[i], llcc_miss_dir); + if (IS_ERR(master_dir)) { + pr_err("Debugfs directory creation failed for %s\n", master_names[i]); + goto error_cleanup; + } + + ret = debugfs_create_file("show_last_samples", 0400, master_dir, + master_names[i], &show_last_samples_ops); + if (IS_ERR(ret)) { + pr_err("Debugfs file creation failed for show_last_samples\n"); + goto error_cleanup; + } + + ret = debugfs_create_file("enable", 0644, master_dir, + master_names[i], &set_mon_enabled_ops); + if (IS_ERR(ret)) { + pr_err("Debugfs file creation failed for enable\n"); + goto error_cleanup; + } + } + + return 0; + +error_cleanup: + for (; i >= 0; i--) + debugfs_remove_recursive(debugfs_lookup(master_names[i], llcc_miss_dir)); + debugfs_remove_recursive(llcc_miss_dir); + + return -ENOENT; +} + +static int __init qcom_llcc_miss_init(void) +{ + int ret, i, j; + + llcc_miss = kzalloc(sizeof(*llcc_miss), GFP_KERNEL); + if (!llcc_miss) + return -ENOMEM; + llcc_miss->data = kzalloc(sizeof(*llcc_miss->data), GFP_KERNEL); + if (!llcc_miss->data) { + kfree(llcc_miss); + return -ENOMEM; + } + for (i = 0; i < MAX_MASTER; i++) + llcc_miss->available_masters |= BIT(i); + ret = llcc_miss_create_fs_entries(); + if (ret < 0) + goto err; + + /* + * to get no of hex char in a line multiplying size of struct miss_sample by 2 + * and adding 1 for tab and 1 for newline. + * output format is qtime followed by miss data in hexa,example -> 32c7bd2 a + */ + llcc_miss->size_of_line = sizeof(struct miss_sample) * 2 + 2; + llcc_miss->max_samples = PAGE_SIZE / llcc_miss->size_of_line; + for (i = 0; i < MAX_MASTER; i++) { + mdata[i].miss_data = kcalloc(llcc_miss->max_samples, + sizeof(struct miss_sample), GFP_KERNEL); + if (!mdata[i].miss_data) { + ret = -ENOMEM; + goto debugfs_file_err; + } + } + + mutex_init(&llcc_miss->lock); + hrtimer_init(&llcc_miss->hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + llcc_miss->hrtimer.function = hrtimer_handler; + llcc_miss->wq = create_freezable_workqueue("llcc_miss_wq"); + if (!llcc_miss->wq) { + pr_err("Couldn't create llcc_miss workqueue.\n"); + ret = -ENOMEM; + goto debugfs_file_err; + } + + INIT_WORK(&llcc_miss->work, &llcc_miss_update_work); + + return ret; + +debugfs_file_err: + for (j = 0; j < i; j++) + kfree(mdata[j].miss_data); + debugfs_remove_recursive(llcc_miss_dir); +err: + kfree(llcc_miss->data); + kfree(llcc_miss); + + return ret; +} + +module_init(qcom_llcc_miss_init); + +MODULE_DESCRIPTION("QCOM LLCC_MISS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/dcvs/trace-bus-prof.c b/drivers/soc/qcom/dcvs/trace-bus-prof.c new file mode 100644 index 000000000000..2f7988eaaae9 --- /dev/null +++ b/drivers/soc/qcom/dcvs/trace-bus-prof.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define CREATE_TRACE_POINTS +#include "trace-bus-prof.h" + + +EXPORT_TRACEPOINT_SYMBOL(memory_miss_last_sample); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/dcvs/trace-bus-prof.h b/drivers/soc/qcom/dcvs/trace-bus-prof.h new file mode 100644 index 000000000000..9bd5b02c9522 --- /dev/null +++ b/drivers/soc/qcom/dcvs/trace-bus-prof.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM bus_prof + +#if !defined(_TRACE_BUS_PROF_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_BUS_PROF_H +#include "bus_prof.h" +#include + +TRACE_EVENT(memory_miss_last_sample, + + TP_PROTO(u64 qtime, struct llcc_miss_buf *master_buf0, struct llcc_miss_buf *master_buf1), + + TP_ARGS(qtime, master_buf0, master_buf1), + + TP_STRUCT__entry( + __field(u64, qtime) + __field(u8, master1) + __field(u16, miss1) + __field(u8, master2) + __field(u16, miss2) + ), + + TP_fast_assign( + __entry->qtime = qtime; + __entry->master1 = master_buf0->master_id; + __entry->miss1 = master_buf0->miss_info; + __entry->master2 = master_buf1->master_id; + __entry->miss2 = master_buf1->miss_info; + ), + + TP_printk("qtime=%llu master1=%u miss1=%u master2=%u miss2=%u", + __entry->qtime, + __entry->master1, + __entry->miss1, + __entry->master2, + __entry->miss2) +); +#endif /* _TRACE_BUS_PROF_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/soc/qcom/dcvs + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace-bus-prof + +#include diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c index e6e65c985daf..8fe14a60da1d 100644 --- a/drivers/soc/qcom/smcinvoke.c +++ b/drivers/soc/qcom/smcinvoke.c @@ -2851,6 +2851,9 @@ static int smcinvoke_probe(struct platform_device *pdev) unsigned int count = 1; int rc = 0; + if (!qcom_scm_is_available()) + return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "qcom_scm is not up!\n"); + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (rc) { pr_err("dma_set_mask_and_coherent failed %d\n", rc); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 5769e9478c94..f31449a2901b 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -119,6 +119,8 @@ extern int qcom_scm_pas_shutdown_retry(u32 peripheral); extern bool qcom_scm_pas_supported(u32 peripheral); extern int qcom_scm_get_sec_dump_state(u32 *dump_state); +extern int qcom_scm_get_llcc_missrate(phys_addr_t in_buf, size_t in_buf_size, + phys_addr_t out_buf, size_t out_buf_size); extern int qcom_scm_assign_dump_table_region(bool is_assign, phys_addr_t addr, size_t size); extern int qcom_scm_tz_blsp_modify_owner(int food, u64 subsystem, int *out); diff --git a/modules.list.neo_la b/modules.list.neo_la index 16a15f9e401f..196475bb6997 100644 --- a/modules.list.neo_la +++ b/modules.list.neo_la @@ -54,6 +54,8 @@ msm_geni_serial.ko msm_rtb.ko mem_buf.ko mem_buf_dev.ko +qseecom-mod.ko +smcinvoke_mod.ko pinctrl-neo.ko pinctrl-msm.ko phy-generic.ko diff --git a/modules.list.neo_le b/modules.list.neo_le index c16dbff95fd2..ac7c7f0163bb 100644 --- a/modules.list.neo_le +++ b/modules.list.neo_le @@ -59,6 +59,7 @@ nvmem_qcom-spmi-sdam.ko pinctrl-spmi-gpio.ko qcom-spmi-temp-alarm.ko clk-spmi-pmic-div.ko +qcom_pm8008-regulator.ko sdhci-msm.ko core_hang_detect.ko kryo_arm64_edac.ko