Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f39b111 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss: Add changes to enable VBATT feature"

parents 417ff18c 3cd0f884
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ Optional properties:
			   uS.
  - qcom,icnss-vadc: VADC handle for vph_pwr read APIs.
  - qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs.
  - io-channels: IIO channel to monitor for vph_pwr power.
  - io-channel-names: IIO channel name as per the client name.
  - qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
  - qcom,wlan-msa-fixed-region: phandle, specifier pairs to children of /reserved-memory
  - qcom,hyp_disabled: Boolean context flag to disable hyperviser
+186 −0
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@
#include <linux/ipc_logging.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/adc-tm-clients.h>
#include <linux/iio/consumer.h>
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include <linux/etherdevice.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -790,6 +793,136 @@ int icnss_call_driver_uevent(struct icnss_priv *priv,
	return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
}


static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
{
	int ret = 0;
	int result;

	if (!priv->channel) {
		icnss_pr_err("Channel doesn't exists\n");
		ret = -EINVAL;
		goto out;
	}

	ret = iio_read_channel_processed(penv->channel, &result);
	if (ret < 0) {
		icnss_pr_err("Error reading channel, ret = %d\n", ret);
		goto out;
	}

	*result_uv = (uint64_t) result;
out:
	return ret;
}

static void icnss_vph_notify(enum adc_tm_state state, void *ctx)
{
	struct icnss_priv *priv = ctx;
	uint64_t vph_pwr = 0;
	uint64_t vph_pwr_prev;
	int ret = 0;
	bool update = true;

	if (!priv) {
		icnss_pr_err("Priv pointer is NULL\n");
		return;
	}

	vph_pwr_prev = priv->vph_pwr;

	ret = icnss_get_phone_power(priv, &vph_pwr);
	if (ret < 0)
		return;

	if (vph_pwr < ICNSS_THRESHOLD_LOW) {
		if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
			update = false;
		priv->vph_monitor_params.state_request =
			ADC_TM_HIGH_THR_ENABLE;
		priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
			ICNSS_THRESHOLD_GUARD;
		priv->vph_monitor_params.low_thr = 0;
	} else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
		if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
			update = false;
		priv->vph_monitor_params.state_request =
			ADC_TM_LOW_THR_ENABLE;
		priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
			ICNSS_THRESHOLD_GUARD;
		priv->vph_monitor_params.high_thr = 0;
	} else {
		if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
		    vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
			update = false;
		priv->vph_monitor_params.state_request =
			ADC_TM_HIGH_LOW_THR_ENABLE;
		priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
		priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
	}

	priv->vph_pwr = vph_pwr;

	if (update) {
		icnss_send_vbatt_update(priv, vph_pwr);
		icnss_pr_dbg("set low threshold to %d, high threshold to %d Phone power=%llu\n",
			     priv->vph_monitor_params.low_thr,
			     priv->vph_monitor_params.high_thr, vph_pwr);
	}

	ret = adc_tm5_channel_measure(priv->adc_tm_dev,
				      &priv->vph_monitor_params);
	if (ret)
		icnss_pr_err("TM channel setup failed %d\n", ret);
}

static int icnss_setup_vph_monitor(struct icnss_priv *priv)
{
	int ret = 0;

	if (!priv->adc_tm_dev) {
		icnss_pr_err("ADC TM handler is NULL\n");
		ret = -EINVAL;
		goto out;
	}

	priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
	priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
	priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
	priv->vph_monitor_params.channel = ADC_VBAT_SNS;
	priv->vph_monitor_params.btm_ctx = priv;
	priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
	icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
		     priv->vph_monitor_params.low_thr,
		     priv->vph_monitor_params.high_thr);

	ret = adc_tm5_channel_measure(priv->adc_tm_dev,
				      &priv->vph_monitor_params);
	if (ret)
		icnss_pr_err("TM channel setup failed %d\n", ret);
out:
	return ret;
}

static int icnss_init_vph_monitor(struct icnss_priv *priv)
{
	int ret = 0;

	ret = icnss_get_phone_power(priv, &priv->vph_pwr);
	if (ret < 0)
		goto out;

	icnss_pr_dbg("Phone power=%llu\n", priv->vph_pwr);

	icnss_send_vbatt_update(priv, priv->vph_pwr);

	ret = icnss_setup_vph_monitor(priv);
	if (ret)
		goto out;
out:
	return ret;
}

static int icnss_driver_event_server_arrive(void *data)
{
	int ret = 0;
@@ -859,6 +992,9 @@ static int icnss_driver_event_server_arrive(void *data)
	if (!penv->fw_early_crash_irq)
		register_early_crash_notifications(&penv->pdev->dev);

	if (penv->vbatt_supported)
		icnss_init_vph_monitor(penv);

	return ret;

err_setup_msa:
@@ -882,6 +1018,10 @@ static int icnss_driver_event_server_exit(void *data)

	icnss_clear_server(penv);

	if (penv->adc_tm_dev && penv->vbatt_supported)
		adc_tm5_disable_chan_meas(penv->adc_tm_dev,
					  &penv->vph_monitor_params);

	return 0;
}

@@ -3045,6 +3185,45 @@ static void icnss_debugfs_destroy(struct icnss_priv *priv)
	debugfs_remove_recursive(priv->root_dentry);
}


static int icnss_get_vbatt_info(struct icnss_priv *priv)
{
	struct adc_tm_chip *adc_tm_dev = NULL;
	struct iio_channel *channel = NULL;
	int ret = 0;

	adc_tm_dev = get_adc_tm(&priv->pdev->dev, "icnss");
	if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
		icnss_pr_err("adc_tm_dev probe defer\n");
		return -EPROBE_DEFER;
	}

	if (IS_ERR(adc_tm_dev)) {
		ret = PTR_ERR(adc_tm_dev);
		icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
			     ret);
		return ret;
	}

	channel = iio_channel_get(&priv->pdev->dev, "icnss");
	if (PTR_ERR(channel) == -EPROBE_DEFER) {
		icnss_pr_err("channel probe defer\n");
		return -EPROBE_DEFER;
	}

	if (IS_ERR(channel)) {
		ret = PTR_ERR(channel);
		icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
			     ret);
		return ret;
	}

	priv->adc_tm_dev = adc_tm_dev;
	priv->channel = channel;

	return 0;
}

static int icnss_probe(struct platform_device *pdev)
{
	int ret = 0;
@@ -3074,6 +3253,13 @@ static int icnss_probe(struct platform_device *pdev)

	priv->vreg_info = icnss_vreg_info;

	if (of_property_read_bool(pdev->dev.of_node, "qcom,icnss-adc_tm")) {
		ret = icnss_get_vbatt_info(priv);
		if (ret == -EPROBE_DEFER)
			goto out;
		priv->vbatt_supported = true;
	}

	for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
		ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);

+11 −0
Original line number Diff line number Diff line
@@ -13,6 +13,9 @@
#ifndef __ICNSS_PRIVATE_H__
#define __ICNSS_PRIVATE_H__

#include <linux/adc-tm-clients.h>
#include <linux/iio/consumer.h>

#define icnss_ipc_log_string(_x...) do {				\
	if (icnss_ipc_log_context)					\
		ipc_log_string(icnss_ipc_log_context, _x);		\
@@ -239,6 +242,9 @@ struct icnss_stats {
	uint32_t rejuvenate_ack_req;
	uint32_t rejuvenate_ack_resp;
	uint32_t rejuvenate_ack_err;
	uint32_t vbatt_req;
	uint32_t vbatt_resp;
	uint32_t vbatt_req_err;
};

#define WLFW_MAX_TIMESTAMP_LEN 32
@@ -356,6 +362,11 @@ struct icnss_priv {
	uint32_t fw_error_fatal_irq;
	uint32_t fw_early_crash_irq;
	struct completion unblock_shutdown;
	struct adc_tm_param vph_monitor_params;
	struct adc_tm_chip *adc_tm_dev;
	struct iio_channel *channel;
	uint64_t vph_pwr;
	bool vbatt_supported;
	char function_name[WLFW_FUNCTION_NAME_LEN + 1];
};

+70 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <soc/qcom/service-notifier.h>
#include "wlan_firmware_service_v01.h"
#include "icnss_private.h"
#include "icnss_qmi.h"

#ifdef CONFIG_ICNSS_DEBUG
unsigned long qmi_timeout = 3000;
@@ -1324,4 +1325,73 @@ int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
	return wlfw_wlan_mode_send_sync_msg(priv, mode);
}

int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv)
{
	int ret;
	struct wlfw_vbatt_req_msg_v01 *req;
	struct wlfw_vbatt_resp_msg_v01 *resp;
	struct qmi_txn txn;

	if (!priv)
		return -ENODEV;

	if (test_bit(ICNSS_FW_DOWN, &priv->state))
		return -EINVAL;

	icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n", priv->state);

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req)
		return -ENOMEM;

	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
	if (!resp) {
		kfree(req);
		return -ENOMEM;
	}

	priv->stats.vbatt_req++;

	req->voltage_uv = voltage_uv;

	ret = qmi_txn_init(&priv->qmi, &txn, wlfw_vbatt_resp_msg_v01_ei, resp);
	if (ret < 0) {
		icnss_pr_err("Fail to init txn for Vbatt message resp %d\n",
			     ret);
		goto out;
	}

	ret = qmi_send_request(&priv->qmi, NULL, &txn,
			       QMI_WLFW_VBATT_REQ_V01,
			       WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN,
			       wlfw_vbatt_req_msg_v01_ei, req);
	if (ret < 0) {
		qmi_txn_cancel(&txn);
		icnss_pr_err("Fail to send Vbatt message req %d\n", ret);
		goto out;
	}

	ret = qmi_txn_wait(&txn, WLFW_TIMEOUT);
	if (ret < 0) {
		icnss_pr_err("VBATT message resp wait failed with ret %d\n",
				    ret);
		goto out;
	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
		icnss_pr_err("QMI Vbatt message request rejected, result:%d error:%d\n",
				    resp->resp.result, resp->resp.error);
		ret = -resp->resp.result;
		goto out;
	}

	priv->stats.vbatt_resp++;

	kfree(resp);
	kfree(req);
	return 0;

out:
	kfree(resp);
	kfree(req);
	priv->stats.vbatt_req_err++;
	return ret;
}
+7 −1
Original line number Diff line number Diff line
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -92,6 +92,11 @@ static inline int icnss_register_fw_service(struct icnss_priv *priv)
	return 0;
}
static inline void icnss_unregister_fw_service(struct icnss_priv *priv) {}
static inline int icnss_send_vbatt_update(struct icnss_priv *priv,
					  uint64_t voltage_uv)
{
	return 0;
}

#else
int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv);
@@ -121,6 +126,7 @@ int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv);
int icnss_register_fw_service(struct icnss_priv *priv);
void icnss_unregister_fw_service(struct icnss_priv *priv);
int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv);
#endif

#endif /* __ICNSS_QMI_H__*/