diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index 02900d90851c..1f8e6e4fbdc7 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -169,6 +169,17 @@ config QTI_SCMI_C1DCVS_PROTOCOL This driver defines the comands or message ID's used for this communication and also exposes the ops used by clients. +config QTI_SCMI_PLH_PROTOCOL + tristate "Qualcomm Technologies, Inc. SCMI PLH vendor Protocol" + depends on ARM_SCMI_PROTOCOL && QCOM_CPUCP + help + System Control and Management Interface (SCMI) plh vendor protocol + this protocol provides interface to communicate with micro controller + which is executing the plh algorithm. + + This driver defines the comands or message ID's used for this + communication and also exposes the ops used by clients. + endif #ARM_SCMI_PROTOCOL config ARM_SCMI_POWER_DOMAIN diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 852ca1d71f5a..ec46b5bd709c 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o obj-$(CONFIG_QTI_SCMI_PMU_PROTOCOL) += pmu_vendor.o obj-$(CONFIG_QTI_SCMI_C1DCVS_PROTOCOL) += c1dcvs_vendor.o obj-$(CONFIG_QTI_SCMI_VENDOR_PROTOCOL) += qcom_scmi_vendor.o +obj-$(CONFIG_QTI_SCMI_PLH_PROTOCOL) += plh_vendor.o ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy) # The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame diff --git a/drivers/firmware/arm_scmi/plh_vendor.c b/drivers/firmware/arm_scmi/plh_vendor.c new file mode 100644 index 000000000000..02b0896e3215 --- /dev/null +++ b/drivers/firmware/arm_scmi/plh_vendor.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include "common.h" + +#define SCMI_VENDOR_MSG_MAX_TX_SIZE (100) /* in bytes */ +#define SCMI_VENDOR_MSG_START (3) /* MSG 3-15 can be used for spl purpose */ +#define SCMI_VENDOR_MSG_SPLH_START (16) /* Each PLH module to use MAX 16 MSG */ +#define SCMI_VENDOR_MSG_SPLH_END (31) +#define SCMI_VENDOR_MSG_LPLH_START (32) /* Each PLH module to use MAX 16 MSG */ +#define SCMI_VENDOR_MSG_LPLH_END (47) + +enum scmi_plh_protocol_cmd { + PERF_LOCK_SCROLL_INIT_IPC_FREQ_TBL_MSG_ID = SCMI_VENDOR_MSG_SPLH_START, + PERF_LOCK_SCROLL_START_MSG_ID, + PERF_LOCK_SCROLL_STOP_MSG_ID, + PERF_LOCK_SCROLL_SET_SAMPLE_MS, + PERF_LOCK_SCROLL_SET_LOG_LEVEL, + PERF_LOCK_SCROLL_MAX_MSG_ID = SCMI_VENDOR_MSG_SPLH_END, + PERF_LOCK_LAUNCH_INIT_IPC_FREQ_TBL_MSG_ID = SCMI_VENDOR_MSG_LPLH_START, + PERF_LOCK_LAUNCH_START_MSG_ID, + PERF_LOCK_LAUNCH_STOP_MSG_ID, + PERF_LOCK_LAUNCH_SET_SAMPLE_MS, + PERF_LOCK_LAUNCH_SET_LOG_LEVEL, + PERF_LOCK_LAUNCH_MAX_MSG_ID = SCMI_VENDOR_MSG_LPLH_END, +}; + + +static int scmi_plh_init_ipc_freq_tbl(const struct scmi_protocol_handle *ph, + u16 *p_init_args, u16 init_len, enum plh_features feature) +{ + uint32_t *msg, msg_size, msg_val, align_init_len = init_len; + struct scmi_xfer *t; + int ret, i = 0; + + if (init_len % 2) + align_init_len += 1; /* align in multiple of u32 */ + + msg_size = align_init_len * sizeof(*p_init_args); + + if (msg_size > SCMI_VENDOR_MSG_MAX_TX_SIZE) + return -EINVAL; + + if (feature == PERF_LOCK_SCROLL) + ret = ph->xops->xfer_get_init(ph, PERF_LOCK_SCROLL_INIT_IPC_FREQ_TBL_MSG_ID, + (msg_size), sizeof(uint32_t), &t); + else if (feature == PERF_LOCK_LAUNCH) + ret = ph->xops->xfer_get_init(ph, PERF_LOCK_LAUNCH_INIT_IPC_FREQ_TBL_MSG_ID, + (msg_size), sizeof(uint32_t), &t); + else + return -EINVAL; + + if (ret) + return ret; + + msg = t->tx.buf; + + for (i = 0; i < init_len/2 ; i++) { + msg_val = *p_init_args++; + msg_val |= ((*p_init_args++) << 16); + *msg++ = cpu_to_le32(msg_val); + } + + if (init_len % 2) + *msg = cpu_to_le32(*p_init_args); + + ret = ph->xops->do_xfer(ph, t); + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_plh_set_u16_val(const struct scmi_protocol_handle *ph, + u16 val, u32 msg_id) +{ + int ret = 0; + struct scmi_xfer *t; + uint32_t *msg; + + ret = ph->xops->xfer_get_init(ph, msg_id, + sizeof(*msg), sizeof(uint32_t), &t); + if (ret) + return ret; + + msg = t->tx.buf; + *msg = cpu_to_le32(val); + ret = ph->xops->do_xfer(ph, t); + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_plh_start_cmd(const struct scmi_protocol_handle *ph, + u16 value, enum plh_features feature) +{ + int ret = -EINVAL; + + if (feature == PERF_LOCK_SCROLL) + ret = scmi_plh_set_u16_val(ph, value, PERF_LOCK_SCROLL_START_MSG_ID); + else if (feature == PERF_LOCK_LAUNCH) + ret = scmi_plh_set_u16_val(ph, value, PERF_LOCK_LAUNCH_START_MSG_ID); + + return ret; +} + +static int scmi_plh_stop_cmd(const struct scmi_protocol_handle *ph, enum plh_features feature) +{ + int ret = -EINVAL; + + if (feature == PERF_LOCK_SCROLL) + ret = scmi_plh_set_u16_val(ph, 0, PERF_LOCK_SCROLL_STOP_MSG_ID); + else if (feature == PERF_LOCK_LAUNCH) + ret = scmi_plh_set_u16_val(ph, 0, PERF_LOCK_LAUNCH_STOP_MSG_ID); + + return ret; +} + +static int scmi_plh_set_sample_ms(const struct scmi_protocol_handle *ph, + u16 sample_ms, enum plh_features feature) +{ + int ret = -EINVAL; + + if (feature == PERF_LOCK_SCROLL) + ret = scmi_plh_set_u16_val(ph, sample_ms, PERF_LOCK_SCROLL_SET_SAMPLE_MS); + else if (feature == PERF_LOCK_LAUNCH) + ret = scmi_plh_set_u16_val(ph, sample_ms, PERF_LOCK_LAUNCH_SET_SAMPLE_MS); + + return ret; +} + +static int scmi_plh_set_log_level(const struct scmi_protocol_handle *ph, + u16 log_level, enum plh_features feature) +{ + int ret = -EINVAL; + + if (feature == PERF_LOCK_SCROLL) + ret = scmi_plh_set_u16_val(ph, log_level, PERF_LOCK_SCROLL_SET_LOG_LEVEL); + else if (feature == PERF_LOCK_LAUNCH) + ret = scmi_plh_set_u16_val(ph, log_level, PERF_LOCK_LAUNCH_SET_LOG_LEVEL); + + return ret; +} + +static const struct scmi_plh_vendor_ops plh_proto_ops = { + .init_plh_ipc_freq_tbl = scmi_plh_init_ipc_freq_tbl, + .start_plh = scmi_plh_start_cmd, + .stop_plh = scmi_plh_stop_cmd, + .set_plh_sample_ms = scmi_plh_set_sample_ms, + .set_plh_log_level = scmi_plh_set_log_level, +}; + +static int scmi_plh_vendor_protocol_init(const struct scmi_protocol_handle *ph) +{ + u32 version; + + ph->xops->version_get(ph, &version); + + dev_dbg(ph->dev, "PLH version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + return 0; +} + +static const struct scmi_protocol scmi_plh_vendor = { + .id = SCMI_PROTOCOL_PLH, + .owner = THIS_MODULE, + .instance_init = &scmi_plh_vendor_protocol_init, + .ops = &plh_proto_ops, +}; +module_scmi_protocol(scmi_plh_vendor); + +MODULE_DESCRIPTION("SCMI plh vendor Protocol"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 8a94ffaaf37b..35caebe06b43 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -1018,6 +1018,19 @@ config MSM_PERFORMANCE fmin and fmax. The user space can request the cpu freq change by writing cpu#:freq values +config QTI_PLH_SCMI_CLIENT + tristate "Qualcomm Technologies Inc. SCMI client driver for PLH" + depends on MSM_PERFORMANCE && QTI_SCMI_PLH_PROTOCOL + default n + help + SCMI client driver registers itself with SCMI framework for PLH + vendor protocol, and also registers with the plh interface driver + msm_performance. + + This driver deliver the PLH vendor protocol handle to interface + driver, and interface driver will use this handle to communicate + with CPUCP PLH. + config QCOM_PANEL_EVENT_NOTIFIER tristate "panel event notifier" depends on DRM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index b33e00feb5ef..6c8a06fe7bc2 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_QCOM_CPUCP) += qcom_cpucp.o obj-$(CONFIG_QTI_CPUCP_LOG) += cpucp_log.o obj-$(CONFIG_QTI_SYS_PM_VX) += sys_pm_vx.o obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o +obj-$(CONFIG_QTI_PLH_SCMI_CLIENT) += plh_scmi.o obj-$(CONFIG_QCOM_WCD_USBSS_I2C) += wcd_usbss_i2c.o obj-$(CONFIG_QCOM_SYSMON_SUBSYSTEM_STATS) += sysmon_subsystem_stats.o obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o diff --git a/drivers/soc/qcom/plh_scmi.c b/drivers/soc/qcom/plh_scmi.c new file mode 100644 index 000000000000..fd86bd591036 --- /dev/null +++ b/drivers/soc/qcom/plh_scmi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct scmi_device *scmi_dev; +static int scmi_plh_inited; + +struct scmi_device *get_plh_scmi_device(void) +{ + if (!scmi_plh_inited) + return ERR_PTR(-EPROBE_DEFER); + + if (!scmi_dev || !scmi_dev->handle) + return ERR_PTR(-ENODEV); + + return scmi_dev; +} +EXPORT_SYMBOL(get_plh_scmi_device); + +static int scmi_plh_probe(struct scmi_device *sdev) +{ + int ret = 0; + + scmi_dev = sdev; + if (!sdev) + ret = -ENODEV; + + scmi_plh_inited = 1; + return ret; +} + +static const struct scmi_device_id scmi_id_table[] = { + { .protocol_id = SCMI_PROTOCOL_PLH, .name = "scmi_protocol_plh" }, + { } +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_plh_drv = { + .name = "scmi-plh-driver", + .probe = scmi_plh_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_plh_drv); + +MODULE_SOFTDEP("pre: plh_vendor"); +MODULE_DESCRIPTION("ARM SCMI PLH driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/scmi_plh.h b/include/linux/scmi_plh.h new file mode 100644 index 000000000000..7c4f9e53722d --- /dev/null +++ b/include/linux/scmi_plh.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SCMI Vendor Protocols header + * + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _SCMI_PLH_VENDOR_H +#define _SCMI_PLH_VENDOR_H + +#include +#include +#include + +#define SCMI_PROTOCOL_PLH 0x81 + +enum plh_features { + PERF_LOCK_SCROLL, + PERF_LOCK_LAUNCH, +}; + +struct scmi_protocol_handle; +extern struct scmi_device *get_plh_scmi_device(void); + +/** + * struct scmi_plh_vendor_ops - represents the various operations provided + * by SCMI PLH Protocol + * + * @init_plh_ipc_freq_tbl: initialize plh ipc freq voting table in cpucp + * @start_plh: starts plh in cpucp + * @stop_plh: stops plh in cpucp + * @set_plh_sample_ms: configure the sampling duration of plh in cpucp + * @set_plh_log_level: configure the supported log_level of plh in cpucp + */ +struct scmi_plh_vendor_ops { + int (*init_plh_ipc_freq_tbl)(const struct scmi_protocol_handle *ph, + u16 *p_init_args, u16 init_len, enum plh_features feature); + int (*start_plh)(const struct scmi_protocol_handle *ph, u16 fps, enum plh_features feature); + int (*stop_plh)(const struct scmi_protocol_handle *ph, enum plh_features feature); + int (*set_plh_sample_ms)(const struct scmi_protocol_handle *ph, + u16 sample_ms, enum plh_features feature); + int (*set_plh_log_level)(const struct scmi_protocol_handle *ph, + u16 log_level, enum plh_features feature); +}; + +#endif