diff options
| -rw-r--r-- | Documentation/devicetree/bindings/arm/cpus.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/sram/qcom,imem.yaml | 15 | ||||
| -rw-r--r-- | drivers/soc/qcom/mdt_loader.c | 63 | ||||
| -rw-r--r-- | drivers/soc/qcom/qcom_stats.c | 133 | ||||
| -rw-r--r-- | drivers/soc/qcom/qmi_encdec.c | 52 | ||||
| -rw-r--r-- | drivers/soc/qcom/qmi_interface.c | 6 | ||||
| -rw-r--r-- | drivers/soc/qcom/socinfo.c | 2 | ||||
| -rw-r--r-- | include/linux/soc/qcom/qmi.h | 6 |
10 files changed, 253 insertions, 27 deletions
diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 2e9ab9583005..5bd517befb68 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -200,6 +200,7 @@ properties: - qcom,kryo385 - qcom,kryo465 - qcom,kryo468 + - qcom,kryo470 - qcom,kryo485 - qcom,kryo560 - qcom,kryo570 diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 41fbbe059d80..b1a786b838d5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -38,6 +38,7 @@ properties: - qcom,sdx75-aoss-qmp - qcom,sdm845-aoss-qmp - qcom,sm6350-aoss-qmp + - qcom,sm7150-aoss-qmp - qcom,sm8150-aoss-qmp - qcom,sm8250-aoss-qmp - qcom,sm8350-aoss-qmp diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml index ce7e20dd22c9..fdc6fc17ed71 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml @@ -18,6 +18,7 @@ properties: compatible: items: - enum: + - qcom,sm7150-dcc - qcom,sm8150-dcc - qcom,sc7280-dcc - qcom,sc7180-dcc diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml index 2711f90d9664..72d35e30c439 100644 --- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -22,17 +22,32 @@ properties: - qcom,msm8974-imem - qcom,msm8976-imem - qcom,qcs404-imem + - qcom,qcs615-imem - qcom,qcs8300-imem - qcom,qdu1000-imem - qcom,sa8775p-imem + - qcom,sar2130p-imem - qcom,sc7180-imem - qcom,sc7280-imem + - qcom,sc8280xp-imem - qcom,sdm630-imem - qcom,sdm845-imem - qcom,sdx55-imem - qcom,sdx65-imem + - qcom,sdx75-imem + - qcom,sm6115-imem + - qcom,sm6125-imem + - qcom,sm6350-imem - qcom,sm6375-imem + - qcom,sm7150-imem + - qcom,sm8150-imem + - qcom,sm8250-imem + - qcom,sm8350-imem - qcom,sm8450-imem + - qcom,sm8550-imem + - qcom,sm8650-imem + - qcom,sm8750-imem + - qcom,x1e80100-imem - const: syscon - const: simple-mfd diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index b2c0fb55d4ae..0ca268bdf1f8 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -18,7 +18,38 @@ #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> -static bool mdt_phdr_valid(const struct elf32_phdr *phdr) +static bool mdt_header_valid(const struct firmware *fw) +{ + const struct elf32_hdr *ehdr; + size_t phend; + size_t shend; + + if (fw->size < sizeof(*ehdr)) + return false; + + ehdr = (struct elf32_hdr *)fw->data; + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) + return false; + + if (ehdr->e_phentsize != sizeof(struct elf32_phdr)) + return false; + + phend = size_add(size_mul(sizeof(struct elf32_phdr), ehdr->e_phnum), ehdr->e_phoff); + if (phend > fw->size) + return false; + + if (ehdr->e_shentsize != sizeof(struct elf32_shdr)) + return false; + + shend = size_add(size_mul(sizeof(struct elf32_shdr), ehdr->e_shnum), ehdr->e_shoff); + if (shend > fw->size) + return false; + + return true; +} + +static bool mdt_phdr_loadable(const struct elf32_phdr *phdr) { if (phdr->p_type != PT_LOAD) return false; @@ -82,13 +113,16 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw) phys_addr_t max_addr = 0; int i; + if (!mdt_header_valid(fw)) + return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_paddr < min_addr) @@ -134,8 +168,11 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, ssize_t ret; void *data; + if (!mdt_header_valid(fw)) + return ERR_PTR(-EINVAL); + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); if (ehdr->e_phnum < 2) return ERR_PTR(-EINVAL); @@ -214,13 +251,16 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, int ret; int i; + if (!mdt_header_valid(fw)) + return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_flags & QCOM_MDT_RELOCATABLE) @@ -270,7 +310,7 @@ static bool qcom_mdt_bins_are_split(const struct firmware *fw, const char *fw_na int i; ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { /* @@ -310,14 +350,17 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!fw || !mem_region || !mem_phys || !mem_size) return -EINVAL; + if (!mdt_header_valid(fw)) + return -EINVAL; + is_split = qcom_mdt_bins_are_split(fw, fw_name); ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_flags & QCOM_MDT_RELOCATABLE) @@ -344,7 +387,7 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; offset = phdr->p_paddr - mem_reloc; diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 5de99cf59b9f..2e380faf9080 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved. */ +#include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/io.h> @@ -11,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/seq_file.h> +#include <linux/soc/qcom/qcom_aoss.h> #include <linux/soc/qcom/smem.h> #include <clocksource/arm_arch_timer.h> @@ -24,6 +27,19 @@ #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 +#define DDR_STATS_NUM_MODES_ADDR 0x4 +#define DDR_STATS_ENTRY_START_ADDR 0x8 + +#define DDR_STATS_CP_IDX(data) FIELD_GET(GENMASK(4, 0), data) +#define DDR_STATS_LPM_NAME(data) FIELD_GET(GENMASK(7, 0), data) +#define DDR_STATS_TYPE(data) FIELD_GET(GENMASK(15, 8), data) +#define DDR_STATS_FREQ(data) FIELD_GET(GENMASK(31, 16), data) + +static struct qmp *qcom_stats_qmp; + struct subsystem_data { const char *name; u32 smem_item; @@ -48,12 +64,19 @@ static const struct subsystem_data subsystems[] = { struct stats_config { size_t stats_offset; + size_t ddr_stats_offset; size_t num_records; bool appended_stats_avail; bool dynamic_offset; bool subsystem_stats_in_smem; }; +struct ddr_stats_entry { + u32 name; + u32 count; + u64 duration; +}; + struct stats_data { bool appended_stats_avail; void __iomem *base; @@ -122,8 +145,101 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused) return 0; } +static void qcom_ddr_stats_print(struct seq_file *s, struct ddr_stats_entry *data) +{ + u32 cp_idx; + + /* + * DDR statistic have two different types of details encoded. + * (1) DDR LPM Stats + * (2) DDR Frequency Stats + * + * The name field have details like which type of DDR stat (bits 8:15) + * along with other details as explained below + * + * In case of DDR LPM stat, name field will be encoded as, + * Bits - Meaning + * 0:7 - DDR LPM name, can be of 0xd4, 0xd3, 0x11 and 0xd0. + * 8:15 - 0x0 (indicates its a LPM stat) + * 16:31 - Unused + * + * In case of DDR FREQ stats, name field will be encoded as, + * Bits - Meaning + * 0:4 - DDR Clock plan index (CP IDX) + * 5:7 - Unused + * 8:15 - 0x1 (indicates its Freq stat) + * 16:31 - Frequency value in Mhz + */ + switch (DDR_STATS_TYPE(data->name)) { + case 0: + seq_printf(s, "DDR LPM Stat Name:0x%lx\tcount:%u\tDuration (ticks):%llu\n", + DDR_STATS_LPM_NAME(data->name), data->count, data->duration); + break; + case 1: + if (!data->count || !DDR_STATS_FREQ(data->name)) + return; + + cp_idx = DDR_STATS_CP_IDX(data->name); + seq_printf(s, "DDR Freq %luMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n", + DDR_STATS_FREQ(data->name), cp_idx, data->count, data->duration); + break; + } +} + +static int qcom_ddr_stats_show(struct seq_file *s, void *d) +{ + struct ddr_stats_entry data[DDR_STATS_MAX_NUM_MODES]; + void __iomem *reg = (void __iomem *)s->private; + u32 entry_count; + int i, ret; + + entry_count = readl_relaxed(reg + DDR_STATS_NUM_MODES_ADDR); + if (entry_count > DDR_STATS_MAX_NUM_MODES) + return -EINVAL; + + if (qcom_stats_qmp) { + /* + * Recent SoCs (SM8450 onwards) do not have duration field + * populated from boot up onwards for both DDR LPM Stats + * and DDR Frequency Stats. + * + * Send QMP message to Always on processor which will + * populate duration field into MSG RAM area. + * + * Sent every time to read latest data. + */ + ret = qmp_send(qcom_stats_qmp, "{class: ddr, action: freqsync}"); + if (ret) + return ret; + } + + reg += DDR_STATS_ENTRY_START_ADDR; + memcpy_fromio(data, reg, sizeof(struct ddr_stats_entry) * entry_count); + + for (i = 0; i < entry_count; i++) + qcom_ddr_stats_print(s, &data[i]); + + return 0; +} + DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats); DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats); +DEFINE_SHOW_ATTRIBUTE(qcom_ddr_stats); + +static void qcom_create_ddr_stat_files(struct dentry *root, void __iomem *reg, + const struct stats_config *config) +{ + u32 key; + + if (!config->ddr_stats_offset) + return; + + key = readl_relaxed(reg + config->ddr_stats_offset + DDR_STATS_MAGIC_KEY_ADDR); + if (key == DDR_STATS_MAGIC_KEY) + debugfs_create_file("ddr_stats", 0400, root, + (__force void *)reg + config->ddr_stats_offset, + &qcom_ddr_stats_fops); +} static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg, struct stats_data *d, @@ -207,11 +323,27 @@ static int qcom_stats_probe(struct platform_device *pdev) for (i = 0; i < config->num_records; i++) d[i].appended_stats_avail = config->appended_stats_avail; + /* + * QMP is used for DDR stats syncing to MSG RAM for recent SoCs (SM8450 onwards). + * The prior SoCs do not need QMP handle as the required stats are already present + * in MSG RAM, provided the DDR_STATS_MAGIC_KEY matches. + */ + qcom_stats_qmp = qmp_get(&pdev->dev); + if (IS_ERR(qcom_stats_qmp)) { + /* We ignore error if QMP is not defined/needed */ + if (!of_property_present(pdev->dev.of_node, "qcom,qmp")) + qcom_stats_qmp = NULL; + else if (PTR_ERR(qcom_stats_qmp) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else + return PTR_ERR(qcom_stats_qmp); + } root = debugfs_create_dir("qcom_stats", NULL); qcom_create_subsystem_stat_files(root, config); qcom_create_soc_sleep_stat_files(root, reg, d, config); + qcom_create_ddr_stat_files(root, reg, config); platform_set_drvdata(pdev, root); @@ -254,6 +386,7 @@ static const struct stats_config rpmh_data_sdm845 = { static const struct stats_config rpmh_data = { .stats_offset = 0x48, + .ddr_stats_offset = 0xb8, .num_records = 3, .appended_stats_avail = false, .dynamic_offset = false, diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index bb09eff85cff..7660a960fb45 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -304,6 +304,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, const void *buf_src; int encode_tlv = 0; int rc; + u8 val8; + u16 val16; if (!ei_array) return 0; @@ -338,7 +340,6 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, break; case QMI_DATA_LEN: - memcpy(&data_len_value, buf_src, temp_ei->elem_size); data_len_sz = temp_ei->elem_size == sizeof(u8) ? sizeof(u8) : sizeof(u16); /* Check to avoid out of range buffer access */ @@ -348,8 +349,17 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, __func__); return -ETOOSMALL; } - rc = qmi_encode_basic_elem(buf_dst, &data_len_value, - 1, data_len_sz); + if (data_len_sz == sizeof(u8)) { + val8 = *(u8 *)buf_src; + data_len_value = (u32)val8; + rc = qmi_encode_basic_elem(buf_dst, &val8, + 1, data_len_sz); + } else { + val16 = *(u16 *)buf_src; + data_len_value = (u32)le16_to_cpu(val16); + rc = qmi_encode_basic_elem(buf_dst, &val16, + 1, data_len_sz); + } UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, encoded_bytes, tlv_len, encode_tlv, rc); @@ -523,14 +533,23 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array, u32 string_len = 0; u32 string_len_sz = 0; const struct qmi_elem_info *temp_ei = ei_array; + u8 val8; + u16 val16; if (dec_level == 1) { string_len = tlv_len; } else { string_len_sz = temp_ei->elem_len <= U8_MAX ? sizeof(u8) : sizeof(u16); - rc = qmi_decode_basic_elem(&string_len, buf_src, - 1, string_len_sz); + if (string_len_sz == sizeof(u8)) { + rc = qmi_decode_basic_elem(&val8, buf_src, + 1, string_len_sz); + string_len = (u32)val8; + } else { + rc = qmi_decode_basic_elem(&val16, buf_src, + 1, string_len_sz); + string_len = (u32)val16; + } decoded_bytes += rc; } @@ -604,6 +623,9 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, u32 decoded_bytes = 0; const void *buf_src = in_buf; int rc; + u8 val8; + u16 val16; + u32 val32; while (decoded_bytes < in_buf_len) { if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) @@ -642,9 +664,17 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, if (temp_ei->data_type == QMI_DATA_LEN) { data_len_sz = temp_ei->elem_size == sizeof(u8) ? sizeof(u8) : sizeof(u16); - rc = qmi_decode_basic_elem(&data_len_value, buf_src, - 1, data_len_sz); - memcpy(buf_dst, &data_len_value, sizeof(u32)); + if (data_len_sz == sizeof(u8)) { + rc = qmi_decode_basic_elem(&val8, buf_src, + 1, data_len_sz); + data_len_value = (u32)val8; + } else { + rc = qmi_decode_basic_elem(&val16, buf_src, + 1, data_len_sz); + data_len_value = (u32)val16; + } + val32 = cpu_to_le32(data_len_value); + memcpy(buf_dst, &val32, sizeof(u32)); temp_ei = temp_ei + 1; buf_dst = out_c_struct + temp_ei->offset; tlv_len -= data_len_sz; @@ -746,9 +776,9 @@ void *qmi_encode_message(int type, unsigned int msg_id, size_t *len, hdr = msg; hdr->type = type; - hdr->txn_id = txn_id; - hdr->msg_id = msg_id; - hdr->msg_len = msglen; + hdr->txn_id = cpu_to_le16(txn_id); + hdr->msg_id = cpu_to_le16(msg_id); + hdr->msg_len = cpu_to_le16(msglen); *len = sizeof(*hdr) + msglen; diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index bc6d6379d8b1..6500f863aae5 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -400,7 +400,7 @@ static void qmi_invoke_handler(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, for (handler = qmi->handlers; handler->fn; handler++) { if (handler->type == hdr->type && - handler->msg_id == hdr->msg_id) + handler->msg_id == le16_to_cpu(hdr->msg_id)) break; } @@ -488,7 +488,7 @@ static void qmi_handle_message(struct qmi_handle *qmi, /* If this is a response, find the matching transaction handle */ if (hdr->type == QMI_RESPONSE) { mutex_lock(&qmi->txn_lock); - txn = idr_find(&qmi->txns, hdr->txn_id); + txn = idr_find(&qmi->txns, le16_to_cpu(hdr->txn_id)); /* Ignore unexpected responses */ if (!txn) { @@ -514,7 +514,7 @@ static void qmi_handle_message(struct qmi_handle *qmi, } else { /* Create a txn based on the txn_id of the incoming message */ memset(&tmp_txn, 0, sizeof(tmp_txn)); - tmp_txn.id = hdr->txn_id; + tmp_txn.id = le16_to_cpu(hdr->txn_id); qmi_invoke_handler(qmi, sq, &tmp_txn, buf, len); } diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 8c4147737c35..391380820f08 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -48,6 +48,7 @@ #define SMEM_IMAGE_TABLE_CDSP1_INDEX 19 #define SMEM_IMAGE_TABLE_GPDSP_INDEX 20 #define SMEM_IMAGE_TABLE_GPDSP1_INDEX 21 +#define SMEM_IMAGE_TABLE_TME_INDEX 28 #define SMEM_IMAGE_VERSION_TABLE 469 /* @@ -67,6 +68,7 @@ static const char *const socinfo_image_names[] = { [SMEM_IMAGE_TABLE_CDSP1_INDEX] = "cdsp1", [SMEM_IMAGE_TABLE_GPDSP_INDEX] = "gpdsp", [SMEM_IMAGE_TABLE_GPDSP1_INDEX] = "gpdsp1", + [SMEM_IMAGE_TABLE_TME_INDEX] = "tme", }; static const char *const pmic_models[] = { diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h index 469e02d2aa0d..291cdc7ef49c 100644 --- a/include/linux/soc/qcom/qmi.h +++ b/include/linux/soc/qcom/qmi.h @@ -24,9 +24,9 @@ struct socket; */ struct qmi_header { u8 type; - u16 txn_id; - u16 msg_id; - u16 msg_len; + __le16 txn_id; + __le16 msg_id; + __le16 msg_len; } __packed; #define QMI_REQUEST 0 |
