From ad222973bf68efd42d40b31cbf5bdf3428395712 Mon Sep 17 00:00:00 2001 From: Ram Chandrasekar Date: Wed, 20 Nov 2019 17:05:18 -0700 Subject: [PATCH] drivers: thermal: qmi_sensor: Add snapshot of qmi sensor driver This is a snapshot of the qmi sensor driver from msm-4.19 to msm-5.4 as of 'commit <076ba8f024a882373ff8ed27780362c3733b21dc> (drivers: thermal: qmi_sensor: Add support for new sensors)'. Change-Id: Ib1e6359aba7a794cf64f07e888cef36b6c04f87c Signed-off-by: Ram Chandrasekar --- drivers/thermal/qcom/Kconfig | 10 + drivers/thermal/qcom/Makefile | 2 + drivers/thermal/qcom/qmi_sensors.c | 724 ++++++++++++++++++ .../thermal/qcom/thermal_sensor_service_v01.c | 253 ++++++ .../thermal/qcom/thermal_sensor_service_v01.h | 82 ++ 5 files changed, 1071 insertions(+) create mode 100644 drivers/thermal/qcom/qmi_sensors.c create mode 100644 drivers/thermal/qcom/thermal_sensor_service_v01.c create mode 100644 drivers/thermal/qcom/thermal_sensor_service_v01.h diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index e041de6e4349..63cc75dbe31c 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -21,6 +21,16 @@ config QCOM_SPMI_TEMP_ALARM real time die temperature if an ADC is present or an estimate of the temperature based upon the over temperature stage value. +config QTI_QMI_SENSOR + tristate "QTI QMI sensor driver" + depends on QCOM_QMI_HELPERS && THERMAL_OF && QTI_THERMAL + help + This enables to list the QTI remote subsystem temperature sensors. + 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. + + config QTI_CPU_ISOLATE_COOLING_DEVICE tristate "QTI CPU Isolate cooling devices" depends on THERMAL_OF && QTI_THERMAL diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 5a1d1b1a7998..75f059c6f285 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -4,4 +4,6 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \ tsens-8960.o tsens-v2.o tsens-v1.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o +obj-$(CONFIG_QTI_QMI_SENSOR) += qti_qmi_sensor.o +qti_qmi_sensor-y += thermal_sensor_service_v01.o qmi_sensors.o obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o diff --git a/drivers/thermal/qcom/qmi_sensors.c b/drivers/thermal/qcom/qmi_sensors.c new file mode 100644 index 000000000000..92e039a850af --- /dev/null +++ b/drivers/thermal/qcom/qmi_sensors.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2019, The Linux Foundation. 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_v01.h" +#include "../thermal_core.h" + +#define QMI_SENS_DRIVER "qmi-therm-sensors" +#define QMI_TS_RESP_TOUT msecs_to_jiffies(100) +#define QMI_CLIENT_NAME_LENGTH 40 +#define QMI_FL_SIGN 0x80000000 +#define QMI_FL_EXP 0x7f800000 +#define QMI_FL_MANTISSA 0x007fffff +#define QMI_FL_NORM 0x00800000 +#define QMI_FL_SIGN_BIT 31 +#define QMI_MANTISSA_MSB 23 + +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_MAX_NR +}; + +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 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"}, +}; + +static int32_t encode_qmi(int32_t val) +{ + uint32_t shift = 0, local_val = 0; + int32_t temp_val = 0; + + if (val == INT_MAX || val == INT_MIN) + return 0; + + temp_val = val = val / 1000; + if (val < 0) { + temp_val *= -1; + local_val |= 1 << QMI_FL_SIGN_BIT; + } + shift = find_last_bit((const unsigned long *)&temp_val, + sizeof(temp_val) * 8); + local_val |= ((shift + 127) << QMI_MANTISSA_MSB); + temp_val &= ~(1 << shift); + + local_val |= temp_val << (QMI_MANTISSA_MSB - shift); + pr_debug("inp:%d shift:%d out:%x temp_val:%x\n", + val, shift, local_val, temp_val); + + return local_val; +} + +static int32_t decode_qmi(int32_t val) +{ + int32_t sign = 0, shift = 0, local_val; + + sign = (val & QMI_FL_SIGN) ? -1 : 1; + shift = (val & QMI_FL_EXP) >> QMI_MANTISSA_MSB; + shift = QMI_MANTISSA_MSB - (shift - 127); + local_val = (val & QMI_FL_MANTISSA) | QMI_FL_NORM; + pr_debug("val:0x%x sign:%d shift:%d mantissa:%x temp:%d\n", + val, sign, shift, local_val, + sign * (local_val >> shift)); + + return sign * (local_val >> shift); +} + +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); + + of_thermal_handle_trip_temp(qmi_sens->dev, qmi_sens->tz_dev, + qmi_sens->last_reading); +}; + +static void qmi_ts_update_temperature(struct qmi_ts_instance *ts, + const struct ts_temp_report_ind_msg_v01 *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_V01))) + continue; + + qmi_sens->last_reading = + decode_qmi(ind_msg->temp) * 1000; + 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->high_thresh != INT_MAX && + qmi_sens->last_reading >= qmi_sens->high_thresh) || + (qmi_sens->low_thresh != INT_MIN && + 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(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *decoded) +{ + const struct ts_temp_report_ind_msg_v01 *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_V01) || + 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_request(struct qmi_sensor *qmi_sens, + bool send_current_temp_report) +{ + int ret = 0; + struct ts_register_notification_temp_resp_msg_v01 resp; + struct ts_register_notification_temp_req_msg_v01 req; + struct qmi_ts_instance *ts = qmi_sens->ts; + struct qmi_txn txn; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + strlcpy(req.sensor_id.sensor_id, qmi_sens->qmi_name, + QMI_TS_SENSOR_ID_LENGTH_MAX_V01); + 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 = + encode_qmi(qmi_sens->high_thresh); + req.temp_threshold_low_valid = + qmi_sens->low_thresh != INT_MIN; + req.temp_threshold_low = + encode_qmi(qmi_sens->low_thresh); + } + + mutex_lock(&ts->mutex); + + ret = qmi_txn_init(&ts->handle, &txn, + ts_register_notification_temp_resp_msg_v01_ei, &resp); + if (ret < 0) { + pr_err("qmi txn init failed for %s ret:%d\n", + qmi_sens->qmi_name, ret); + goto qmi_send_exit; + } + + ret = qmi_send_request(&ts->handle, NULL, &txn, + QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V01, + TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V01_MAX_MSG_LEN, + ts_register_notification_temp_req_msg_v01_ei, &req); + if (ret < 0) { + pr_err("qmi txn send failed for %s ret:%d\n", + qmi_sens->qmi_name, ret); + qmi_txn_cancel(&txn); + goto qmi_send_exit; + } + + ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT); + if (ret < 0) { + pr_err("qmi txn wait failed for %s ret:%d\n", + qmi_sens->qmi_name, ret); + goto qmi_send_exit; + } + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ret = resp.resp.result; + pr_err("qmi NOT success for %s ret:%d\n", + qmi_sens->qmi_name, ret); + goto qmi_send_exit; + } + ret = 0; + +qmi_send_exit: + mutex_unlock(&ts->mutex); + 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) + 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_V01, + .ei = ts_temp_report_ind_msg_v01_ei, + .decoded_size = sizeof(struct ts_temp_report_ind_msg_v01), + .fn = qmi_ts_ind_cb + }, + {} +}; + +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_v01 req; + struct ts_get_sensor_list_resp_msg_v01 *ts_resp; + int ret = 0, i; + struct qmi_txn txn; + + memset(&req, 0, sizeof(req)); + /* size of ts_resp is very high, use heap memory rather than stack */ + ts_resp = kzalloc(sizeof(*ts_resp), GFP_KERNEL); + if (!ts_resp) + return -ENOMEM; + + mutex_lock(&ts->mutex); + ret = qmi_txn_init(&ts->handle, &txn, + ts_get_sensor_list_resp_msg_v01_ei, ts_resp); + if (ret < 0) { + pr_err("Transaction Init error for inst_id:0x%x ret:%d\n", + ts->inst_id, ret); + goto reg_exit; + } + + ret = qmi_send_request(&ts->handle, NULL, &txn, + QMI_TS_GET_SENSOR_LIST_REQ_V01, + TS_GET_SENSOR_LIST_REQ_MSG_V01_MAX_MSG_LEN, + ts_get_sensor_list_req_msg_v01_ei, + &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + goto reg_exit; + } + + ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT); + if (ret < 0) { + pr_err("Transaction wait error for inst_id:0x%x ret:%d\n", + ts->inst_id, ret); + goto reg_exit; + } + if (ts_resp->resp.result != QMI_RESULT_SUCCESS_V01) { + ret = ts_resp->resp.result; + pr_err("Get sensor list NOT success for inst_id:0x%x ret:%d\n", + ts->inst_id, ret); + goto reg_exit; + } + mutex_unlock(&ts->mutex); + + for (i = 0; i < ts_resp->sensor_list_len; i++) { + struct qmi_sensor *qmi_sens = NULL; + + list_for_each_entry(qmi_sens, &ts->ts_sensor_list, + ts_node) { + if ((strncasecmp(qmi_sens->qmi_name, + ts_resp->sensor_list[i].sensor_id, + QMI_TS_SENSOR_ID_LENGTH_MAX_V01))) + 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); + break; + } + } + + kfree(ts_resp); + return ret; + +reg_exit: + mutex_unlock(&ts->mutex); + kfree(ts_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); + 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); + + 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); + strlcpy(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_MIN; + 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_V01_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); + goto probe_err; + } + ret = qmi_add_lookup(&ts->handle, TS_SERVICE_ID_V01, + TS_SERVICE_VERS_V01, ts->inst_id); + if (ret < 0) { + dev_err(dev, "QMI register failed for 0x%x, ret:%d\n", + ts->inst_id, ret); + 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 v2"); diff --git a/drivers/thermal/qcom/thermal_sensor_service_v01.c b/drivers/thermal/qcom/thermal_sensor_service_v01.c new file mode 100644 index 000000000000..9cae54a23a2d --- /dev/null +++ b/drivers/thermal/qcom/thermal_sensor_service_v01.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include "thermal_sensor_service_v01.h" + +static struct qmi_elem_info ts_sensor_type_v01_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = QMI_TS_SENSOR_ID_LENGTH_MAX_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ts_sensor_type_v01, + sensor_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_sensor_list_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_get_sensor_list_resp_msg_v01_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_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = + offsetof(struct ts_get_sensor_list_resp_msg_v01, + sensor_list_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = + offsetof(struct ts_get_sensor_list_resp_msg_v01, + sensor_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_TS_SENSOR_LIST_MAX_V01, + .elem_size = sizeof(struct ts_sensor_type_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = + offsetof(struct ts_get_sensor_list_resp_msg_v01, + sensor_list), + .ei_array = ts_sensor_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_register_notification_temp_req_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ts_sensor_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + sensor_id), + .ei_array = ts_sensor_type_v01_ei, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + send_current_temp_report), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + 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_v01, + temp_threshold_high), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + 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_v01, + temp_threshold_low), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + seq_num_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof( + struct ts_register_notification_temp_req_msg_v01, + seq_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_register_notification_temp_resp_msg_v01_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_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ts_temp_report_ind_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ts_sensor_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ts_temp_report_ind_msg_v01, + sensor_id), + .ei_array = ts_sensor_type_v01_ei, + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum ts_temp_report_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ts_temp_report_ind_msg_v01, + report_type), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ts_temp_report_ind_msg_v01, + 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_v01, + temp), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_temp_report_ind_msg_v01, + seq_num_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct ts_temp_report_ind_msg_v01, + seq_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + diff --git a/drivers/thermal/qcom/thermal_sensor_service_v01.h b/drivers/thermal/qcom/thermal_sensor_service_v01.h new file mode 100644 index 000000000000..b66f14b5c2d9 --- /dev/null +++ b/drivers/thermal/qcom/thermal_sensor_service_v01.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + */ + +#ifndef THERMAL_SENSOR_SERVICE_V01_H +#define THERMAL_SENSOR_SERVICE_V01_H + +#define TS_SERVICE_ID_V01 0x17 +#define TS_SERVICE_VERS_V01 0x01 + +#define QMI_TS_GET_SENSOR_LIST_RESP_V01 0x0020 +#define QMI_TS_GET_SUPPORTED_MSGS_REQ_V01 0x001E +#define QMI_TS_GET_SUPPORTED_MSGS_RESP_V01 0x001E +#define QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V01 0x0021 +#define QMI_TS_REGISTER_NOTIFICATION_TEMP_RESP_V01 0x0021 +#define QMI_TS_GET_SUPPORTED_FIELDS_RESP_V01 0x001F +#define QMI_TS_GET_SENSOR_LIST_REQ_V01 0x0020 +#define QMI_TS_TEMP_REPORT_IND_V01 0x0022 +#define QMI_TS_GET_SUPPORTED_FIELDS_REQ_V01 0x001F + +#define QMI_TS_SENSOR_ID_LENGTH_MAX_V01 32 +#define QMI_TS_SENSOR_LIST_MAX_V01 32 + +struct ts_sensor_type_v01 { + char sensor_id[QMI_TS_SENSOR_ID_LENGTH_MAX_V01 + 1]; +}; + +struct ts_get_sensor_list_req_msg_v01 { + char placeholder; +}; +#define TS_GET_SENSOR_LIST_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct qmi_elem_info ts_get_sensor_list_req_msg_v01_ei[]; + +struct ts_get_sensor_list_resp_msg_v01 { + struct qmi_response_type_v01 resp; + uint8_t sensor_list_valid; + uint32_t sensor_list_len; + struct ts_sensor_type_v01 sensor_list[QMI_TS_SENSOR_LIST_MAX_V01]; +}; +#define TS_GET_SENSOR_LIST_RESP_MSG_V01_MAX_MSG_LEN 1067 +extern struct qmi_elem_info ts_get_sensor_list_resp_msg_v01_ei[]; + +struct ts_register_notification_temp_req_msg_v01 { + struct ts_sensor_type_v01 sensor_id; + uint8_t send_current_temp_report; + uint8_t temp_threshold_high_valid; + int temp_threshold_high; + uint8_t temp_threshold_low_valid; + int temp_threshold_low; + uint8_t seq_num_valid; + uint32_t seq_num; +}; +#define TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V01_MAX_MSG_LEN 61 +extern struct qmi_elem_info ts_register_notification_temp_req_msg_v01_ei[]; + +struct ts_register_notification_temp_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; +#define TS_REGISTER_NOTIFICATION_TEMP_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct qmi_elem_info ts_register_notification_temp_resp_msg_v01_ei[]; + +enum ts_temp_report_type_enum_v01 { + TS_TEMP_REPORT_TYPE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_TS_TEMP_REPORT_CURRENT_TEMP_V01 = 0, + QMI_TS_TEMP_REPORT_THRESHOLD_HIGH_V01 = 1, + QMI_TS_TEMP_REPORT_THRESHOLD_LOW_V01 = 2, + TS_TEMP_REPORT_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +struct ts_temp_report_ind_msg_v01 { + struct ts_sensor_type_v01 sensor_id; + enum ts_temp_report_type_enum_v01 report_type; + uint8_t temp_valid; + long temp; + uint8_t seq_num_valid; + uint32_t seq_num; +}; +#define TS_TEMP_REPORT_IND_MSG_V01_MAX_MSG_LEN 57 +extern struct qmi_elem_info ts_temp_report_ind_msg_v01_ei[]; + +#endif