From ec5bc9a94b6dbc3034d75aba0adb7d8b31bb4750 Mon Sep 17 00:00:00 2001 From: Rashid Zafar Date: Fri, 4 Nov 2022 15:33:37 -0700 Subject: [PATCH] drivers: thermal: qmi_sensor: Add snapshot of QMI sensor V2 driver This is a snapshot of the thermal framework from msm-5.10 as of commit f5a9cf8e4b17 ("drivers: qmi_sensor: Re-configure qmi sensor trip on subsystem restart"). Updates: - Change GPL v2 license to GPL Change-Id: If2681995379274a5d9d641d0dce2b4f9ec07edb6 Signed-off-by: Manaf Meethalavalappu Pallikunhi Signed-off-by: Rashid Zafar --- drivers/thermal/qcom/Kconfig | 10 + drivers/thermal/qcom/Makefile | 3 + drivers/thermal/qcom/qmi_sensors.h | 147 ++++ drivers/thermal/qcom/qmi_sensors_v2.c | 654 ++++++++++++++++++ .../thermal/qcom/thermal_sensor_service_v02.c | 286 ++++++++ .../thermal/qcom/thermal_sensor_service_v02.h | 98 +++ 6 files changed, 1198 insertions(+) create mode 100644 drivers/thermal/qcom/qmi_sensors.h create mode 100644 drivers/thermal/qcom/qmi_sensors_v2.c create mode 100644 drivers/thermal/qcom/thermal_sensor_service_v02.c create mode 100644 drivers/thermal/qcom/thermal_sensor_service_v02.h diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index bc8189d09eba..494fa9956531 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -82,3 +82,13 @@ config QTI_POLICY_ENGINE_SENSOR a sensor. This will enable to provide configuration to mitigate cooling devices when a recommendation is sent from hardware. +config QTI_QMI_SENSOR_V2 + tristate "QTI QMI TS V2 sensor driver" + depends on THERMAL_OF + select QCOM_QMI_HELPERS + help + This enables to list the QTI remote subsystem temperature sensors + with QMI TS V2 interface. This driver can read the temperature of + the remote sensor. These sensors can take thresholds and notify the + thermal framework when the threshold is reached. + diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 4f5540bd724c..5d5182621e2e 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -10,3 +10,6 @@ obj-$(CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE) += thermal_pause.o obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o obj-$(CONFIG_QTI_POLICY_ENGINE_SENSOR) += policy_engine.o + +obj-$(CONFIG_QTI_QMI_SENSOR_V2) += qti_qmi_sensor_v2.o +qti_qmi_sensor_v2-y += thermal_sensor_service_v02.o qmi_sensors_v2.o diff --git a/drivers/thermal/qcom/qmi_sensors.h b/drivers/thermal/qcom/qmi_sensors.h new file mode 100644 index 000000000000..776c18d81052 --- /dev/null +++ b/drivers/thermal/qcom/qmi_sensors.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __QMI_SENSORS_H__ +#define __QMI_SENSORS_H__ + +#define QMI_CLIENT_NAME_LENGTH 40 + +enum qmi_ts_sensor { + QMI_TS_PA, + QMI_TS_PA_1, + QMI_TS_PA_2, + QMI_TS_QFE_PA_0, + QMI_TS_QFE_WTR_0, + QMI_TS_MODEM_MODEM, + QMI_TS_MMW_0, + QMI_TS_MMW_1, + QMI_TS_MMW_2, + QMI_TS_MMW_3, + QMI_TS_MODEM_SKIN, + QMI_TS_QFE_PA_MDM, + QMI_TS_QFE_PA_WTR, + QMI_TS_STREAMER_0, + QMI_TS_MOD_MMW_0, + QMI_TS_MOD_MMW_1, + QMI_TS_MOD_MMW_2, + QMI_TS_MOD_MMW_3, + QMI_TS_RET_PA_0, + QMI_TS_WTR_PA_0, + QMI_TS_WTR_PA_1, + QMI_TS_WTR_PA_2, + QMI_TS_WTR_PA_3, + QMI_SYS_THERM1, + QMI_SYS_THERM2, + QMI_TS_TSENS_1, + QMI_TS_MMW_PA1, + QMI_TS_MMW_PA2, + QMI_TS_MMW_PA3, + QMI_TS_SDR_MMW, + QMI_TS_QTM_THERM, + QMI_TS_BCL_WARN, + QMI_TS_SDR0_PA0, + QMI_TS_SDR0_PA1, + QMI_TS_SDR0_PA2, + QMI_TS_SDR0_PA3, + QMI_TS_SDR0_PA4, + QMI_TS_SDR0_PA5, + QMI_TS_SDR0, + QMI_TS_SDR1_PA0, + QMI_TS_SDR1_PA1, + QMI_TS_SDR1_PA2, + QMI_TS_SDR1_PA3, + QMI_TS_SDR1_PA4, + QMI_TS_SDR1_PA5, + QMI_TS_SDR1, + QMI_TS_MMW0, + QMI_TS_MMW1, + QMI_TS_MMW2, + QMI_TS_MMW3, + QMI_TS_MMW_IFIC0, + QMI_TS_SUB1_MODEM_CFG, + QMI_TS_SUB1_LTE_CC, + QMI_TS_SUB1_MCG_FR1_CC, + QMI_TS_SUB1_MCG_FR2_CC, + QMI_TS_SUB1_SCG_FR1_CC, + QMI_TS_SUB1_SCG_FR2_CC, + QMI_TS_SUB2_MODEM_CFG, + QMI_TS_SUB2_LTE_CC, + QMI_TS_SUB2_MCG_FR1_CC, + QMI_TS_SUB2_MCG_FR2_CC, + QMI_TS_SUB2_SCG_FR1_CC, + QMI_TS_SUB2_SCG_FR2_CC, + QMI_TS_NSP_ISENSE_TRIM, + QMI_TS_MAX_NR +}; + +static char sensor_clients[QMI_TS_MAX_NR][QMI_CLIENT_NAME_LENGTH] = { + {"pa"}, + {"pa_1"}, + {"pa_2"}, + {"qfe_pa0"}, + {"qfe_wtr0"}, + {"modem_tsens"}, + {"qfe_mmw0"}, + {"qfe_mmw1"}, + {"qfe_mmw2"}, + {"qfe_mmw3"}, + {"xo_therm"}, + {"qfe_pa_mdm"}, + {"qfe_pa_wtr"}, + {"qfe_mmw_streamer0"}, + {"qfe_mmw0_mod"}, + {"qfe_mmw1_mod"}, + {"qfe_mmw2_mod"}, + {"qfe_mmw3_mod"}, + {"qfe_ret_pa0"}, + {"qfe_wtr_pa0"}, + {"qfe_wtr_pa1"}, + {"qfe_wtr_pa2"}, + {"qfe_wtr_pa3"}, + {"sys_therm1"}, + {"sys_therm2"}, + {"modem_tsens1"}, + {"mmw_pa1"}, + {"mmw_pa2"}, + {"mmw_pa3"}, + {"sdr_mmw_therm"}, + {"qtm_therm"}, + {"modem_bcl_warn"}, + {"sdr0_pa0"}, + {"sdr0_pa1"}, + {"sdr0_pa2"}, + {"sdr0_pa3"}, + {"sdr0_pa4"}, + {"sdr0_pa5"}, + {"sdr0"}, + {"sdr1_pa0"}, + {"sdr1_pa1"}, + {"sdr1_pa2"}, + {"sdr1_pa3"}, + {"sdr1_pa4"}, + {"sdr1_pa5"}, + {"sdr1"}, + {"mmw0"}, + {"mmw1"}, + {"mmw2"}, + {"mmw3"}, + {"mmw_ific0"}, + {"sub1_modem_cfg"}, + {"sub1_lte_cc"}, + {"sub1_mcg_fr1_cc"}, + {"sub1_mcg_fr2_cc"}, + {"sub1_scg_fr1_cc"}, + {"sub1_scg_fr2_cc"}, + {"sub2_modem_cfg"}, + {"sub2_lte_cc"}, + {"sub2_mcg_fr1_cc"}, + {"sub2_mcg_fr2_cc"}, + {"sub2_scg_fr1_cc"}, + {"sub2_scg_fr2_cc"}, + {"isense_trim"}, +}; + +#endif /* __QMI_SENSORS_H__ */ diff --git a/drivers/thermal/qcom/qmi_sensors_v2.c b/drivers/thermal/qcom/qmi_sensors_v2.c new file mode 100644 index 000000000000..3c2ec59c137b --- /dev/null +++ b/drivers/thermal/qcom/qmi_sensors_v2.c @@ -0,0 +1,654 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "thermal_sensor_service_v02.h" +#include "qmi_sensors.h" + +#define QMI_SENS_DRIVER "qmi-therm-sensors-v2" +#define QMI_TS_RESP_TOUT msecs_to_jiffies(100) + +struct qmi_sensor { + struct device *dev; + char qmi_name[QMI_CLIENT_NAME_LENGTH]; + bool connection_active; + struct list_head ts_node; + struct thermal_zone_device *tz_dev; + int32_t last_reading; + int32_t high_thresh; + int32_t low_thresh; + struct qmi_ts_instance *ts; + enum qmi_ts_sensor sens_type; + struct work_struct therm_notify_work; +}; + +struct qmi_ts_instance { + struct device *dev; + struct qmi_handle handle; + struct mutex mutex; + uint32_t inst_id; + struct list_head ts_sensor_list; + struct work_struct svc_arrive_work; +}; + +static struct qmi_ts_instance *ts_instances; +static int ts_inst_cnt; +static atomic_t in_suspend; + +static int qmi_sensor_pm_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + case PM_SUSPEND_PREPARE: + atomic_set(&in_suspend, 1); + break; + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + atomic_set(&in_suspend, 0); + break; + default: + break; + } + return 0; +} + +static struct notifier_block qmi_sensor_pm_nb = { + .notifier_call = qmi_sensor_pm_notify, +}; + +static void qmi_ts_thresh_notify(struct work_struct *work) +{ + struct qmi_sensor *qmi_sens = container_of(work, + struct qmi_sensor, + therm_notify_work); + + thermal_zone_device_update(qmi_sens->tz_dev, THERMAL_TRIP_VIOLATED); +}; + +static void qmi_ts_update_temperature(struct qmi_ts_instance *ts, + const struct ts_temp_report_ind_msg_v02 *ind_msg, + uint8_t notify) +{ + struct qmi_sensor *qmi_sens; + + list_for_each_entry(qmi_sens, &ts->ts_sensor_list, + ts_node) { + if ((strncasecmp(qmi_sens->qmi_name, + ind_msg->sensor_id.sensor_id, + QMI_TS_SENSOR_ID_LENGTH_MAX_V02))) + continue; + + qmi_sens->last_reading = ind_msg->temp; + + pr_debug("sensor:%s temperature:%d\n", + qmi_sens->qmi_name, qmi_sens->last_reading); + if (!qmi_sens->tz_dev) + return; + if (notify && + (qmi_sens->last_reading >= qmi_sens->high_thresh || + qmi_sens->last_reading <= qmi_sens->low_thresh)) { + pr_debug("Sensor:%s Notify. temp:%d\n", + ind_msg->sensor_id.sensor_id, + qmi_sens->last_reading); + queue_work(system_highpri_wq, + &qmi_sens->therm_notify_work); + } + return; + } +} + +void qmi_ts_ind_cb_v02(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *decoded) +{ + const struct ts_temp_report_ind_msg_v02 *ind_msg = decoded; + uint8_t notify = 0; + struct qmi_ts_instance *ts = container_of(qmi, struct qmi_ts_instance, + handle); + + if (!txn) { + pr_err("Invalid transaction\n"); + return; + } + + if ((ind_msg->report_type != QMI_TS_TEMP_REPORT_CURRENT_TEMP_V02) || + ind_msg->seq_num_valid) + notify = 1; + + if (ind_msg->temp_valid) + qmi_ts_update_temperature(ts, ind_msg, notify); + else + pr_err("Error invalid temperature field.\n"); +} + +static int qmi_ts_send_request(struct qmi_ts_instance *ts, int msg_id, + size_t len, struct qmi_elem_info *req_ei, + struct qmi_elem_info *resp_ei, + void *req, void *resp, + struct qmi_response_type_v01 *resp_data) +{ + int ret = 0; + struct qmi_txn txn; + + mutex_lock(&ts->mutex); + ret = qmi_txn_init(&ts->handle, &txn, resp_ei, resp); + if (ret < 0) { + pr_err("qmi txn init failed for msg id:0x%x ret:%d\n", + msg_id, ret); + goto qmi_ts_send_exit; + } + + ret = qmi_send_request(&ts->handle, NULL, &txn, msg_id, len, + req_ei, req); + if (ret < 0) { + pr_err("qmi txn send failed for msg id:0x%x ret:%d\n", + msg_id, ret); + qmi_txn_cancel(&txn); + goto qmi_ts_send_exit; + } + + ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT); + if (ret < 0) { + pr_err("qmi txn wait failed for msg id:0x%x ret:%d\n", + msg_id, ret); + goto qmi_ts_send_exit; + } + if (resp_data->result != QMI_RESULT_SUCCESS_V01) { + ret = resp_data->result; + pr_err("qmi NOT success for msg id:0x%x ret:%d\n", + msg_id, ret); + goto qmi_ts_send_exit; + } + ret = 0; + +qmi_ts_send_exit: + mutex_unlock(&ts->mutex); + return ret; +} + +static int qmi_ts_request(struct qmi_sensor *qmi_sens, + bool send_current_temp_report) +{ + int ret = 0; + struct ts_register_notification_temp_resp_msg_v02 resp; + struct ts_register_notification_temp_req_msg_v02 req; + struct qmi_ts_instance *ts = qmi_sens->ts; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + strscpy(req.sensor_id.sensor_id, qmi_sens->qmi_name, + QMI_TS_SENSOR_ID_LENGTH_MAX_V02); + req.seq_num = 0; + if (send_current_temp_report) { + req.send_current_temp_report = 1; + req.seq_num_valid = true; + } else { + req.seq_num_valid = false; + req.temp_threshold_high_valid = + qmi_sens->high_thresh != INT_MAX; + req.temp_threshold_high = qmi_sens->high_thresh; + req.temp_threshold_low_valid = + qmi_sens->low_thresh != (-INT_MAX); + req.temp_threshold_low = qmi_sens->low_thresh; + pr_debug("sensor:%s high_valid:%d high:%d low_valid:%d low:%d\n", + req.sensor_id.sensor_id, req.temp_threshold_high_valid, + req.temp_threshold_high, req.temp_threshold_low_valid, + req.temp_threshold_low); + } + + ret = qmi_ts_send_request(ts, QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V02, + TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V02_MAX_MSG_LEN, + ts_register_notification_temp_req_msg_v02_ei, + ts_register_notification_temp_resp_msg_v02_ei, + &req, &resp, &resp.resp); + if (ret < 0) + pr_err("qmi ts txn failed for %s ret:%d\n", + qmi_sens->qmi_name, ret); + + return ret; +} + +static int qmi_sensor_read(void *data, int *temp) +{ + struct qmi_sensor *qmi_sens = (struct qmi_sensor *)data; + + if (qmi_sens->connection_active && !atomic_read(&in_suspend)) + qmi_ts_request(qmi_sens, true); + *temp = qmi_sens->last_reading; + + return 0; +} + +static int qmi_sensor_set_trips(void *data, int low, int high) +{ + struct qmi_sensor *qmi_sens = (struct qmi_sensor *)data; + int ret = 0; + + if (qmi_sens->high_thresh == high && + qmi_sens->low_thresh == low) + return ret; + qmi_sens->high_thresh = high; + qmi_sens->low_thresh = low; + if (!qmi_sens->connection_active) + return ret; + ret = qmi_ts_request(qmi_sens, false); + if (ret < 0) + pr_err("Sensor:%s set high trip:%d low trip:%d error%d\n", + qmi_sens->qmi_name, + qmi_sens->high_thresh, + qmi_sens->low_thresh, + ret); + + return ret; +} + +static struct thermal_zone_of_device_ops qmi_sensor_ops = { + .get_temp = qmi_sensor_read, + .set_trips = qmi_sensor_set_trips, +}; + +static struct qmi_msg_handler handlers[] = { + { + .type = QMI_INDICATION, + .msg_id = QMI_TS_TEMP_REPORT_IND_V02, + .ei = ts_temp_report_ind_msg_v02_ei, + .decoded_size = sizeof(struct ts_temp_report_ind_msg_v02), + .fn = qmi_ts_ind_cb_v02 + }, + {} +}; + +static int qmi_register_sensor_device(struct qmi_sensor *qmi_sens) +{ + int ret = 0; + + qmi_sens->tz_dev = thermal_zone_of_sensor_register( + qmi_sens->dev, + qmi_sens->sens_type + qmi_sens->ts->inst_id, + qmi_sens, &qmi_sensor_ops); + if (IS_ERR(qmi_sens->tz_dev)) { + ret = PTR_ERR(qmi_sens->tz_dev); + if (ret != -ENODEV) + pr_err("sensor register failed for %s, ret:%d\n", + qmi_sens->qmi_name, ret); + qmi_sens->tz_dev = NULL; + return ret; + } + pr_debug("Sensor register success for %s\n", qmi_sens->qmi_name); + + return 0; +} + +static int verify_sensor_and_register(struct qmi_ts_instance *ts) +{ + struct ts_get_sensor_list_req_msg_v02 list_req; + struct ts_get_sensor_list_resp_msg_v02 *list_resp; + struct ts_get_total_num_of_sensors_req_msg_v02 tot_req; + struct ts_get_total_num_of_sensors_resp_msg_v02 tot_resp; + int ret = 0, i, j, sensor_list_itr; + + memset(&tot_resp, 0, sizeof(tot_resp)); + memset(&tot_req, 0, sizeof(tot_req)); + /* size of list_resp is very high, use heap memory rather than stack */ + list_resp = kzalloc(sizeof(*list_resp), GFP_KERNEL); + if (!list_resp) + return -ENOMEM; + + ret = qmi_ts_send_request(ts, QMI_TS_GET_TOTAL_NUM_OF_SENSORS_REQ_V02, + TS_GET_TOTAL_NUM_OF_SENSORS_REQ_MSG_V02_MAX_MSG_LEN, + ts_get_total_num_of_sensors_req_msg_v02_ei, + ts_get_total_num_of_sensors_resp_msg_v02_ei, + &tot_req, &tot_resp, &tot_resp.resp); + if (ret < 0) { + pr_err("qmi ts txn failed, inst_id:%d ret:%d\n", + ts->inst_id, ret); + goto reg_exit; + } + + sensor_list_itr = (tot_resp.total_num_sensors / + QMI_TS_SENSOR_LIST_MAX_V02 + + (tot_resp.total_num_sensors % + QMI_TS_SENSOR_LIST_MAX_V02 ? 1 : 0)); + + pr_debug("Total QMI sensors cnt:%d list iteration cnt:%d inst_id:%d\n", + tot_resp.total_num_sensors, sensor_list_itr, ts->inst_id); + + for (i = 0; i < sensor_list_itr; i++) { + memset(&list_req, 0, sizeof(list_req)); + memset(list_resp, 0, sizeof(*list_resp)); + + list_req.list_index = i; + + ret = qmi_ts_send_request(ts, QMI_TS_GET_SENSOR_LIST_REQ_V02, + TS_GET_SENSOR_LIST_REQ_MSG_V02_MAX_MSG_LEN, + ts_get_sensor_list_req_msg_v02_ei, + ts_get_sensor_list_resp_msg_v02_ei, + &list_req, list_resp, &list_resp->resp); + if (ret < 0) { + pr_err("qmi ts txn failed, inst_id:%d itr:%d ret:%d\n", + ts->inst_id, i, ret); + goto reg_exit; + } + + for (j = 0; j < list_resp->sensor_list_len; j++) { + struct qmi_sensor *qmi_sens = NULL; + + pr_debug("QMI TS sensor[%d][%d]: %s\n", i, j, + list_resp->sensor_list[j].sensor_id); + + list_for_each_entry(qmi_sens, &ts->ts_sensor_list, + ts_node) { + if ((strncasecmp(qmi_sens->qmi_name, + list_resp->sensor_list[j].sensor_id, + QMI_TS_SENSOR_ID_LENGTH_MAX_V02))) + continue; + + qmi_sens->connection_active = true; + /* + * Send a temperature request notification. + */ + qmi_ts_request(qmi_sens, true); + if (!qmi_sens->tz_dev) { + ret = qmi_register_sensor_device(qmi_sens); + } else { + ret = qmi_ts_request(qmi_sens, false); + if (ret) + pr_err( + "sensor:%s trips:%d %d err%d\n", + qmi_sens->tz_dev->type, + qmi_sens->high_thresh, + qmi_sens->low_thresh, + ret); + } + break; + } + } + } + +reg_exit: + kfree(list_resp); + return ret; +} + +static void qmi_ts_svc_arrive(struct work_struct *work) +{ + struct qmi_ts_instance *ts = container_of(work, + struct qmi_ts_instance, + svc_arrive_work); + + verify_sensor_and_register(ts); +} + +static void thermal_qmi_net_reset(struct qmi_handle *qmi) +{ + struct qmi_ts_instance *ts = container_of(qmi, + struct qmi_ts_instance, + handle); + struct qmi_sensor *qmi_sens = NULL; + int ret; + + pr_debug("reset QMI server\n"); + list_for_each_entry(qmi_sens, &ts->ts_sensor_list, + ts_node) { + if (!qmi_sens->connection_active) + continue; + qmi_ts_request(qmi_sens, true); + ret = qmi_ts_request(qmi_sens, false); + if (ret) + pr_err("Sensor:%s set high trip:%d low trip:%d err%d\n", + qmi_sens->tz_dev->type, + qmi_sens->high_thresh, + qmi_sens->low_thresh, + ret); + } +} + +static void thermal_qmi_del_server(struct qmi_handle *qmi, + struct qmi_service *service) +{ + struct qmi_ts_instance *ts = container_of(qmi, + struct qmi_ts_instance, + handle); + struct qmi_sensor *qmi_sens = NULL; + + pr_debug("QMI server deleted\n"); + list_for_each_entry(qmi_sens, &ts->ts_sensor_list, ts_node) + qmi_sens->connection_active = false; +} + +static int thermal_qmi_new_server(struct qmi_handle *qmi, + struct qmi_service *service) +{ + struct qmi_ts_instance *ts = container_of(qmi, + struct qmi_ts_instance, + handle); + struct sockaddr_qrtr sq = {AF_QIPCRTR, service->node, service->port}; + + mutex_lock(&ts->mutex); + pr_debug("new QMI server is up, connecting..\n"); + kernel_connect(qmi->sock, (struct sockaddr *)&sq, sizeof(sq), 0); + mutex_unlock(&ts->mutex); + queue_work(system_highpri_wq, &ts->svc_arrive_work); + + return 0; +} + +static struct qmi_ops thermal_qmi_event_ops = { + .new_server = thermal_qmi_new_server, + .del_server = thermal_qmi_del_server, + .net_reset = thermal_qmi_net_reset, +}; + +static void qmi_ts_cleanup(void) +{ + struct qmi_ts_instance *ts; + struct qmi_sensor *qmi_sens, *c_next; + int idx = 0; + + for (; idx < ts_inst_cnt; idx++) { + ts = &ts_instances[idx]; + mutex_lock(&ts->mutex); + list_for_each_entry_safe(qmi_sens, c_next, + &ts->ts_sensor_list, ts_node) { + qmi_sens->connection_active = false; + if (qmi_sens->tz_dev) { + thermal_zone_of_sensor_unregister( + qmi_sens->dev, qmi_sens->tz_dev); + qmi_sens->tz_dev = NULL; + } + + list_del(&qmi_sens->ts_node); + } + qmi_handle_release(&ts->handle); + + mutex_unlock(&ts->mutex); + } + ts_inst_cnt = 0; +} + +static int of_get_qmi_ts_platform_data(struct device *dev) +{ + int ret = 0, i = 0, idx = 0; + struct device_node *np = dev->of_node; + struct device_node *subsys_np = NULL; + struct qmi_ts_instance *ts; + struct qmi_sensor *qmi_sens; + int sens_name_max = 0, sens_idx = 0, subsys_cnt = 0; + + subsys_cnt = of_get_available_child_count(np); + if (!subsys_cnt) { + dev_err(dev, "No child node to process\n"); + return -EFAULT; + } + + ts = devm_kcalloc(dev, subsys_cnt, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + for_each_available_child_of_node(np, subsys_np) { + if (idx >= subsys_cnt) + break; + + ret = of_property_read_u32(subsys_np, "qcom,instance-id", + &ts[idx].inst_id); + if (ret) { + dev_err(dev, "error reading qcom,insance-id. ret:%d\n", + ret); + goto data_fetch_err; + } + + ts[idx].dev = dev; + mutex_init(&ts[idx].mutex); + INIT_LIST_HEAD(&ts[idx].ts_sensor_list); + INIT_WORK(&ts[idx].svc_arrive_work, qmi_ts_svc_arrive); + + sens_name_max = of_property_count_strings(subsys_np, + "qcom,qmi-sensor-names"); + if (sens_name_max <= 0) { + dev_err(dev, "Invalid or no sensor. err:%d\n", + sens_name_max); + ret = -EINVAL; + goto data_fetch_err; + } + + for (sens_idx = 0; sens_idx < sens_name_max; sens_idx++) { + const char *qmi_name; + + qmi_sens = devm_kzalloc(dev, sizeof(*qmi_sens), + GFP_KERNEL); + if (!qmi_sens) { + ret = -ENOMEM; + goto data_fetch_err; + } + + of_property_read_string_index(subsys_np, + "qcom,qmi-sensor-names", sens_idx, + &qmi_name); + strscpy(qmi_sens->qmi_name, qmi_name, + QMI_CLIENT_NAME_LENGTH); + /* Check for supported qmi sensors */ + for (i = 0; i < QMI_TS_MAX_NR; i++) { + if (!strcmp(sensor_clients[i], + qmi_sens->qmi_name)) + break; + } + + if (i >= QMI_TS_MAX_NR) { + dev_err(dev, "Unknown sensor:%s\n", + qmi_sens->qmi_name); + ret = -EINVAL; + goto data_fetch_err; + } + dev_dbg(dev, "QMI sensor:%s available\n", qmi_name); + qmi_sens->sens_type = i; + qmi_sens->ts = &ts[idx]; + qmi_sens->dev = dev; + qmi_sens->last_reading = 0; + qmi_sens->high_thresh = INT_MAX; + qmi_sens->low_thresh = -INT_MAX; + INIT_WORK(&qmi_sens->therm_notify_work, + qmi_ts_thresh_notify); + list_add(&qmi_sens->ts_node, &ts[idx].ts_sensor_list); + } + idx++; + } + ts_instances = ts; + ts_inst_cnt = subsys_cnt; + + return 0; +data_fetch_err: + of_node_put(subsys_np); + return ret; +} + +static int qmi_sens_device_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0, idx = 0; + struct qmi_ts_instance *ts; + + ret = of_get_qmi_ts_platform_data(dev); + if (ret) + goto probe_err; + + if (!ts_instances || !ts_inst_cnt) { + dev_err(dev, "Empty ts instances\n"); + return -EINVAL; + } + + for (; idx < ts_inst_cnt; idx++) { + ts = &ts_instances[idx]; + if (list_empty(&ts->ts_sensor_list)) { + ret = -ENODEV; + goto probe_err; + } + ret = qmi_handle_init(&ts->handle, + TS_GET_SENSOR_LIST_RESP_MSG_V02_MAX_MSG_LEN, + &thermal_qmi_event_ops, handlers); + if (ret < 0) { + dev_err(dev, "QMI[0x%x] handle init failed. err:%d\n", + ts->inst_id, ret); + ts_inst_cnt = idx; + ret = -EPROBE_DEFER; + goto probe_err; + } + ret = qmi_add_lookup(&ts->handle, TS_SERVICE_ID_V02, + TS_SERVICE_VERS_V02, ts->inst_id); + if (ret < 0) { + dev_err(dev, "QMI register failed for 0x%x, ret:%d\n", + ts->inst_id, ret); + ret = -EPROBE_DEFER; + goto probe_err; + } + } + atomic_set(&in_suspend, 0); + register_pm_notifier(&qmi_sensor_pm_nb); + return 0; + +probe_err: + qmi_ts_cleanup(); + return ret; +} + +static int qmi_sens_device_remove(struct platform_device *pdev) +{ + qmi_ts_cleanup(); + unregister_pm_notifier(&qmi_sensor_pm_nb); + + return 0; +} + +static const struct of_device_id qmi_sens_device_match[] = { + {.compatible = "qcom,qmi-sensors"}, + {} +}; + +static struct platform_driver qmi_sens_device_driver = { + .probe = qmi_sens_device_probe, + .remove = qmi_sens_device_remove, + .driver = { + .name = QMI_SENS_DRIVER, + .of_match_table = qmi_sens_device_match, + }, +}; + +module_platform_driver(qmi_sens_device_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/qcom/thermal_sensor_service_v02.c b/drivers/thermal/qcom/thermal_sensor_service_v02.c new file mode 100644 index 000000000000..2d341b2fe773 --- /dev/null +++ b/drivers/thermal/qcom/thermal_sensor_service_v02.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include "thermal_sensor_service_v02.h" + +static struct qmi_elem_info ts_sensor_type_v02_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = QMI_TS_SENSOR_ID_LENGTH_MAX_V02 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ts_sensor_type_v02, + sensor_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_total_num_of_sensors_req_msg_v02_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_total_num_of_sensors_resp_msg_v02_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_get_total_num_of_sensors_resp_msg_v02, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct ts_get_total_num_of_sensors_resp_msg_v02, + total_num_sensors), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_sensor_list_req_msg_v02_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ts_get_sensor_list_req_msg_v02, + list_index), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_sensor_list_resp_msg_v02_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_get_sensor_list_resp_msg_v02, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_get_sensor_list_resp_msg_v02, + sensor_list_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_get_sensor_list_resp_msg_v02, + sensor_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_TS_SENSOR_LIST_MAX_V02, + .elem_size = sizeof(struct ts_sensor_type_v02), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_get_sensor_list_resp_msg_v02, + sensor_list), + .ei_array = ts_sensor_type_v02_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_register_notification_temp_req_msg_v02_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ts_sensor_type_v02), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + sensor_id), + .ei_array = ts_sensor_type_v02_ei, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + send_current_temp_report), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + temp_threshold_high_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(int), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + temp_threshold_high), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + temp_threshold_low_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(int), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + temp_threshold_low), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + seq_num_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct ts_register_notification_temp_req_msg_v02, + seq_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_register_notification_temp_resp_msg_v02_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_register_notification_temp_resp_msg_v02, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_temp_report_ind_msg_v02_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ts_sensor_type_v02), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + sensor_id), + .ei_array = ts_sensor_type_v02_ei, + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum ts_temp_report_type_enum_v02), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + report_type), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + temp_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(int), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + temp), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + seq_num_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_temp_report_ind_msg_v02, + seq_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + diff --git a/drivers/thermal/qcom/thermal_sensor_service_v02.h b/drivers/thermal/qcom/thermal_sensor_service_v02.h new file mode 100644 index 000000000000..b5cf7b6fcc8a --- /dev/null +++ b/drivers/thermal/qcom/thermal_sensor_service_v02.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef THERMAL_SENSOR_SERVICE_V02_H +#define THERMAL_SENSOR_SERVICE_V02_H + +#define TS_SERVICE_ID_V02 0x17 +#define TS_SERVICE_VERS_V02 0x02 + +#define QMI_TS_GET_SUPPORTED_MSGS_RESP_V02 0x001E +#define QMI_TS_GET_SENSOR_LIST_RESP_V02 0x0031 +#define QMI_TS_GET_SUPPORTED_MSGS_REQ_V02 0x001E +#define QMI_TS_GET_TOTAL_NUM_OF_SENSORS_RESP_V02 0x0030 +#define QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V02 0x0032 +#define QMI_TS_REGISTER_NOTIFICATION_TEMP_RESP_V02 0x0032 +#define QMI_TS_GET_TOTAL_NUM_OF_SENSORS_REQ_V02 0x0030 +#define QMI_TS_GET_SUPPORTED_FIELDS_RESP_V02 0x001F +#define QMI_TS_GET_SENSOR_LIST_REQ_V02 0x0031 +#define QMI_TS_TEMP_REPORT_IND_V02 0x0033 +#define QMI_TS_GET_SUPPORTED_FIELDS_REQ_V02 0x001F + +#define QMI_TS_SENSOR_ID_LENGTH_MAX_V02 32 +#define QMI_TS_SENSOR_LIST_MAX_V02 96 + +struct ts_sensor_type_v02 { + char sensor_id[QMI_TS_SENSOR_ID_LENGTH_MAX_V02 + 1]; +}; + +struct ts_get_total_num_of_sensors_req_msg_v02 { + char placeholder; +}; +#define TS_GET_TOTAL_NUM_OF_SENSORS_REQ_MSG_V02_MAX_MSG_LEN 0 +extern struct qmi_elem_info ts_get_total_num_of_sensors_req_msg_v02_ei[]; + +struct ts_get_total_num_of_sensors_resp_msg_v02 { + struct qmi_response_type_v01 resp; + u32 total_num_sensors; +}; +#define TS_GET_TOTAL_NUM_OF_SENSORS_RESP_MSG_V02_MAX_MSG_LEN 14 +extern struct qmi_elem_info ts_get_total_num_of_sensors_resp_msg_v02_ei[]; + +struct ts_get_sensor_list_req_msg_v02 { + u32 list_index; +}; +#define TS_GET_SENSOR_LIST_REQ_MSG_V02_MAX_MSG_LEN 7 +extern struct qmi_elem_info ts_get_sensor_list_req_msg_v02_ei[]; + +struct ts_get_sensor_list_resp_msg_v02 { + struct qmi_response_type_v01 resp; + u8 sensor_list_valid; + u32 sensor_list_len; + struct ts_sensor_type_v02 sensor_list[QMI_TS_SENSOR_LIST_MAX_V02]; +}; +#define TS_GET_SENSOR_LIST_RESP_MSG_V02_MAX_MSG_LEN 3179 +extern struct qmi_elem_info ts_get_sensor_list_resp_msg_v02_ei[]; + +struct ts_register_notification_temp_req_msg_v02 { + struct ts_sensor_type_v02 sensor_id; + u8 send_current_temp_report; + u8 temp_threshold_high_valid; + int temp_threshold_high; + u8 temp_threshold_low_valid; + int temp_threshold_low; + u8 seq_num_valid; + u32 seq_num; +}; +#define TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V02_MAX_MSG_LEN 61 +extern struct qmi_elem_info ts_register_notification_temp_req_msg_v02_ei[]; + +struct ts_register_notification_temp_resp_msg_v02 { + struct qmi_response_type_v01 resp; +}; +#define TS_REGISTER_NOTIFICATION_TEMP_RESP_MSG_V02_MAX_MSG_LEN 7 +extern struct qmi_elem_info ts_register_notification_temp_resp_msg_v02_ei[]; + +enum ts_temp_report_type_enum_v02 { + TS_TEMP_REPORT_TYPE_ENUM_MIN_VAL_V02 = INT_MIN, + QMI_TS_TEMP_REPORT_CURRENT_TEMP_V02 = 0, + QMI_TS_TEMP_REPORT_THRESHOLD_HIGH_V02 = 1, + QMI_TS_TEMP_REPORT_THRESHOLD_LOW_V02 = 2, + TS_TEMP_REPORT_TYPE_ENUM_MAX_VAL_V02 = INT_MAX, +}; + +struct ts_temp_report_ind_msg_v02 { + struct ts_sensor_type_v02 sensor_id; + enum ts_temp_report_type_enum_v02 report_type; + u8 temp_valid; + int temp; + u8 seq_num_valid; + u32 seq_num; +}; +#define TS_TEMP_REPORT_IND_MSG_V02_MAX_MSG_LEN 57 +extern struct qmi_elem_info ts_temp_report_ind_msg_v02_ei[]; + +#endif