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

Commit 9bee67a9 authored by Rama Krishna Phani A's avatar Rama Krishna Phani A
Browse files

hwmon: qpnp-adc: Add thermal sysfs interface to VADC



Add thermal sysfs interface to VADC driver to provide
the thermal clients the ability to read VADC channel
under thermal framework.

Change-Id: Icfe38977579de2aa92ef2ab19a0e949d75ecad79
Signed-off-by: default avatarRama Krishna Phani A <rphani@codeaurora.org>
parent f6fc01a9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -32,6 +32,12 @@ Optional properties:
			   for any one supported channel along with supporting single conversion
			   requests.
- qcom,vadc-recalib-check: Add this property to check if recalibration required due to inaccuracy.
- qcom,vadc-thermal-node : If present a thermal node is created and the channel is registered as
			   part of the thermal sysfs which allows clients to use the thermal framework
			   to set temperature thresholds and receive notification when the temperature
			   crosses a set threshold, read temperature and enable/set trip types supported
			   by the thermal framework.


Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
+97 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>

/* QPNP VADC register definition */
#define QPNP_VADC_REVISION1				0x0
@@ -123,6 +124,14 @@ struct qpnp_vadc_mode_state {
	struct qpnp_adc_amux		vadc_meas_amux;
};

struct qpnp_vadc_thermal_data {
	bool thermal_node;
	int thermal_chan;
	enum qpnp_vadc_channels vadc_channel;
	struct thermal_zone_device *tz_dev;
	struct qpnp_vadc_chip *vadc_dev;
};

struct qpnp_vadc_chip {
	struct device			*dev;
	struct qpnp_adc_drv		*adc;
@@ -143,6 +152,7 @@ struct qpnp_vadc_chip {
	struct work_struct		trigger_high_thr_work;
	struct work_struct		trigger_low_thr_work;
	struct qpnp_vadc_mode_state	*state_copy;
	struct qpnp_vadc_thermal_data	*vadc_therm_chan;
	struct sensor_device_attribute	sens_attr[0];
};

@@ -2123,10 +2133,77 @@ hwmon_err_sens:
	return rc;
}

static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal,
			     unsigned long *temp)
{
	struct qpnp_vadc_thermal_data *vadc_therm = thermal->devdata;
	struct qpnp_vadc_chip *vadc = vadc_therm->vadc_dev;
	struct qpnp_vadc_result result;
	int rc = 0;

	rc = qpnp_vadc_read(vadc,
				vadc_therm->vadc_channel, &result);
	if (rc) {
		pr_err("VADC read error with %d\n", rc);
		return rc;
	}

	*temp = result.physical;

	return rc;
}

static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = {
	.get_temp = qpnp_vadc_get_temp,
};

static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc,
					struct spmi_device *spmi)
{
	struct device_node *child;
	struct device_node *node = spmi->dev.of_node;
	int rc = 0, i = 0;
	bool thermal_node = false;

	if (node == NULL)
		goto thermal_err_sens;
	for_each_child_of_node(node, child) {
		char name[QPNP_THERMALNODE_NAME_LENGTH];

		vadc->vadc_therm_chan[i].vadc_channel =
			vadc->adc->adc_channels[i].channel_num;
		vadc->vadc_therm_chan[i].thermal_chan = i;
		thermal_node = of_property_read_bool(child,
					"qcom,vadc-thermal-node");
		if (thermal_node) {
			/* Register with the thermal zone */
			vadc->vadc_therm_chan[i].thermal_node = true;
			snprintf(name, sizeof(name), "%s",
				vadc->adc->adc_channels[i].name);
			vadc->vadc_therm_chan[i].tz_dev =
				thermal_zone_device_register(name,
				0, 0, &vadc->vadc_therm_chan[i],
				&qpnp_vadc_thermal_ops, NULL, 0, 0);
			if (IS_ERR(vadc->vadc_therm_chan[i].tz_dev)) {
				pr_err("thermal device register failed.\n");
				goto thermal_err_sens;
			}
			vadc->vadc_therm_chan[i].vadc_dev = vadc;
		}
		i++;
		thermal_node = false;
	}
	return 0;
thermal_err_sens:
	pr_err("Init HWMON failed for qpnp_adc with %d\n", rc);
	return rc;
}

static int qpnp_vadc_probe(struct spmi_device *spmi)
{
	struct qpnp_vadc_chip *vadc;
	struct qpnp_adc_drv *adc_qpnp;
	struct qpnp_vadc_thermal_data *adc_thermal;
	struct device_node *node = spmi->dev.of_node;
	struct device_node *child;
	int rc, count_adc_channel_list = 0, i = 0;
@@ -2164,6 +2241,15 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
	}

	vadc->adc = adc_qpnp;
	adc_thermal = devm_kzalloc(&spmi->dev,
			(sizeof(struct qpnp_vadc_thermal_data) *
				count_adc_channel_list), GFP_KERNEL);
	if (!adc_thermal) {
		dev_err(&spmi->dev, "Unable to allocate memory\n");
		return -ENOMEM;
	}

	vadc->vadc_therm_chan = adc_thermal;
	rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc);
	if (rc) {
		dev_err(&spmi->dev, "failed to read device tree\n");
@@ -2177,6 +2263,11 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
		return rc;
	}
	vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
	rc = qpnp_vadc_init_thermal(vadc, spmi);
	if (rc) {
		dev_err(&spmi->dev, "failed to initialize qpnp thermal adc\n");
		return rc;
	}
	vadc->vadc_init_calib = false;
	vadc->max_channels_available = count_adc_channel_list;
	rc = qpnp_vadc_read_reg(vadc, QPNP_INT_TEST_VAL, &fab_id);
@@ -2281,6 +2372,9 @@ err_setup:
	for_each_child_of_node(node, child) {
		device_remove_file(&spmi->dev,
			&vadc->sens_attr[i].dev_attr);
		if (vadc->vadc_therm_chan[i].thermal_node)
			thermal_zone_device_unregister(
					vadc->vadc_therm_chan[i].tz_dev);
		i++;
	}
	hwmon_device_unregister(vadc->vadc_hwmon);
@@ -2298,6 +2392,9 @@ static int qpnp_vadc_remove(struct spmi_device *spmi)
	for_each_child_of_node(node, child) {
		device_remove_file(&spmi->dev,
			&vadc->sens_attr[i].dev_attr);
		if (vadc->vadc_therm_chan[i].thermal_node)
			thermal_zone_device_unregister(
					vadc->vadc_therm_chan[i].tz_dev);
		i++;
	}
	hwmon_device_unregister(vadc->vadc_hwmon);
+2 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2015, 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
@@ -140,6 +140,7 @@ enum qpnp_iadc_channels {
#define QPNP_ADC_625_UV	625000
#define QPNP_ADC_HWMON_NAME_LENGTH				64
#define QPNP_MAX_PROP_NAME_LEN					32
#define QPNP_THERMALNODE_NAME_LENGTH                            25

/* Structure device for qpnp vadc */
struct qpnp_vadc_chip;