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

Commit 5bca648d authored by Ram Chandrasekar's avatar Ram Chandrasekar
Browse files

drivers: thermal: qpnp-temp-alarm: Use of-thermal interface



Update PMIC temp alarm driver to use of-thermal interface to register
the PMIC die temperature sensors. This enables the thermal framework to
define the config for PMIC alarm sensors in the device tree.

PMIC temperature sensors work on predefined thresholds, so it supports
only callback to read temperature. On reaching the predefined threshold
the driver will notify the of-thermal interface to handle the trip
notification.

Change-Id: Ib1cde1edc1a970530e20d19ad041621272b1b93d
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent 24806ed6
Loading
Loading
Loading
Loading
+38 −6
Original line number Diff line number Diff line
@@ -12,10 +12,17 @@ Required properties:
- interrupts:      PMIC temperature alarm interrupt
- label:           A string used as a descriptive name for this thermal device.
		    This name should be 19 characters or less.
- #thermal-sensor-cells: Must be 0. Please refer to
			 <devicetree/bindings/thermal/thermal.txt> for more
			 details.

Required structure:
- A qcom,qpnp-temp-alarm node must be a child of an SPMI node that has specified
   the spmi-slave-container property
- A top level device tree node named "thermal-zones" must exist. It must
   contain a subnode with a property named "thermal-sensors" which is assigned
   a phandle to the qpnp-temp-alarm device node. See
   <devicetree/bindings/thermal/thermal.txt> for more details.

Optional properties:
- qcom,channel-num:    VADC channel number associated PMIC DIE_TEMP thermistor.
@@ -38,11 +45,6 @@ Optional properties:
			 1 = 50 Hz
			 2 = 25 Hz
			 3 = 12.5 Hz
- qcom,allow-override: Boolean which controls the ability of software to
			override shutdowns.  If present, then software is
			allowed to override automatic PMIC hardware stage 2 and
			stage 3 over temperature shutdowns.  Otherwise, software
			is not allowed to override automatic shutdown.
- qcom,default-temp:   Specifies the default temperature in millicelcius to use
			if no ADC channel is present to read the real time
			temperature.
@@ -64,7 +66,7 @@ Example:
		#address-cells = <1>;
		#size-cells = <1>;

		qcom,temp-alarm@2400 {
		pm8941_tz: qcom,temp-alarm@2400 {
			compatible = "qcom,qpnp-temp-alarm";
			reg = <0x2400 0x100>;
			interrupts = <0x0 0x24 0x0>;
@@ -72,6 +74,36 @@ Example:
			qcom,channel-num = <8>;
			qcom,threshold-set = <0>;
			qcom,temp_alarm-vadc = <&pm8941_vadc>;
			#thermal-sensor-cells = <0>;
		};
	};
};

Below is an example thermal zone definition for the temperature alarm
peripheral.
thermal-zones {
	pm8941_tz {
		polling-delay-passive = <0>;
		polling-delay = <0>;
		thermal-governor = "step_wise";
		thermal-sensors = <&pm8941_tz>;

		trips {
			pm8941-trip0 {
				temperature = <105000>;
				hysteresis = <0>;
				type = "passive";
			};
			pm8941-trip1 {
				temperature = <125000>;
				hysteresis = <0>;
				type = "passive";
			};
			pm8941-trip2 {
				temperature = <145000>;
				hysteresis = <0>;
				type = "critical";
			};
		};
	};
};
+14 −203
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <linux/thermal.h>
#include <linux/qpnp/qpnp-adc.h>

#include "thermal_core.h"

#define QPNP_TM_DRIVER_NAME "qcom,qpnp-temp-alarm"

enum qpnp_tm_registers {
@@ -97,7 +99,6 @@ struct qpnp_tm_chip {
	unsigned int			subtype;
	enum qpnp_tm_adc_type		adc_type;
	int				temperature;
	enum thermal_device_mode	mode;
	unsigned int			thresh;
	unsigned int			clock_rate;
	unsigned int			stage;
@@ -105,18 +106,12 @@ struct qpnp_tm_chip {
	int				irq;
	enum qpnp_vadc_channels		adc_channel;
	u16				base_addr;
	bool				allow_software_override;
	struct qpnp_vadc_chip		*vadc_dev;
};

/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
#define STATUS_REGISTER_DELAY_MS       40

enum pmic_thermal_override_mode {
	SOFTWARE_OVERRIDE_DISABLED = 0,
	SOFTWARE_OVERRIDE_ENABLED,
};

/* This array maps from GEN2 alarm state to GEN1 alarm stage */
const unsigned int alarm_state_map[8] = {0, 1, 1, 2, 2, 3, 3, 3};

@@ -156,28 +151,6 @@ static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
	return rc;
}


static inline int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip,
			    enum pmic_thermal_override_mode mode)
{
	int rc = 0;
	u8 reg;

	if (chip->allow_software_override) {
		reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
		reg |= (chip->clock_rate << SHUTDOWN_CTRL1_CLK_RATE_SHIFT)
			& SHUTDOWN_CTRL1_CLK_RATE_MASK;

		if (mode == SOFTWARE_OVERRIDE_ENABLED)
			reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2
				| SHUTDOWN_CTRL1_OVERRIDE_STAGE3;

		rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
	}

	return rc;
}

static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
{
	struct qpnp_vadc_result adc_result;
@@ -274,10 +247,9 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
	return 0;
}

static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
				     int *temperature)
static int qpnp_tz_get_temp_no_adc(void *data, int *temperature)
{
	struct qpnp_tm_chip *chip = thermal->devdata;
	struct qpnp_tm_chip *chip = (struct qpnp_tm_chip *)data;
	int rc;

	if (!temperature)
@@ -292,10 +264,9 @@ static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
	return 0;
}

static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
				      int *temperature)
static int qpnp_tz_get_temp_qpnp_adc(void *data, int *temperature)
{
	struct qpnp_tm_chip *chip = thermal->devdata;
	struct qpnp_tm_chip *chip = (struct qpnp_tm_chip *)data;
	int rc;

	if (!temperature)
@@ -314,121 +285,12 @@ static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
	return 0;
}

static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
			      enum thermal_device_mode *mode)
{
	struct qpnp_tm_chip *chip = thermal->devdata;

	if (!mode)
		return -EINVAL;

	*mode = chip->mode;

	return 0;
}

static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
			      enum thermal_device_mode mode)
{
	struct qpnp_tm_chip *chip = thermal->devdata;
	int rc = 0;

	if (mode != chip->mode) {
		if (mode == THERMAL_DEVICE_ENABLED)
			rc = qpnp_tm_shutdown_override(chip,
				SOFTWARE_OVERRIDE_ENABLED);
		else
			rc = qpnp_tm_shutdown_override(chip,
				SOFTWARE_OVERRIDE_DISABLED);

		chip->mode = mode;
	}

	return rc;
}

static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal,
				   int trip, enum thermal_trip_type *type)
{
	if (trip < 0 || !type)
		return -EINVAL;

	switch (trip) {
	case TRIP_STAGE3:
		*type = THERMAL_TRIP_CRITICAL;
		break;
	case TRIP_STAGE2:
		*type = THERMAL_TRIP_HOT;
		break;
	case TRIP_STAGE1:
		*type = THERMAL_TRIP_HOT;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal,
				   int trip, int *temperature)
{
	struct qpnp_tm_chip *chip = thermal->devdata;
	int thresh_temperature;

	if (trip < 0 || !temperature)
		return -EINVAL;

	thresh_temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;

	switch (trip) {
	case TRIP_STAGE3:
		thresh_temperature += 2 * TEMP_STAGE_STEP;
		break;
	case TRIP_STAGE2:
		thresh_temperature += TEMP_STAGE_STEP;
		break;
	case TRIP_STAGE1:
		break;
	default:
		return -EINVAL;
	}

	*temperature = thresh_temperature;

	return 0;
}

static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
				   int *temperature)
{
	struct qpnp_tm_chip *chip = thermal->devdata;

	if (!temperature)
		return -EINVAL;

	*temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
		2 * TEMP_STAGE_STEP;

	return 0;
}

static struct thermal_zone_device_ops qpnp_thermal_zone_ops_no_adc = {
static struct thermal_zone_of_device_ops qpnp_thermal_zone_ops_no_adc = {
	.get_temp = qpnp_tz_get_temp_no_adc,
	.get_mode = qpnp_tz_get_mode,
	.set_mode = qpnp_tz_set_mode,
	.get_trip_type = qpnp_tz_get_trip_type,
	.get_trip_temp = qpnp_tz_get_trip_temp,
	.get_crit_temp = qpnp_tz_get_crit_temp,
};

static struct thermal_zone_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
static struct thermal_zone_of_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
	.get_temp = qpnp_tz_get_temp_qpnp_adc,
	.get_mode = qpnp_tz_get_mode,
	.set_mode = qpnp_tz_set_mode,
	.get_trip_type = qpnp_tz_get_trip_type,
	.get_trip_temp = qpnp_tz_get_trip_temp,
	.get_crit_temp = qpnp_tz_get_crit_temp,
};

static void qpnp_tm_work(struct work_struct *work)
@@ -474,11 +336,7 @@ static void qpnp_tm_work(struct work_struct *work)
				chip->tm_name, stage_new, chip->stage,
				chip->thresh, chip->temperature);

		thermal_zone_device_update(chip->tz_dev,
						THERMAL_EVENT_UNSPECIFIED);

		/* Notify user space */
		sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
		of_thermal_handle_trip(chip->tz_dev);
	}

bail:
@@ -539,7 +397,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
	struct device_node *node;
	unsigned int base;
	struct qpnp_tm_chip *chip;
	struct thermal_zone_device_ops *tz_ops;
	struct thermal_zone_of_device_ops *tz_ops;
	char *tm_name;
	u32 default_temperature;
	int rc = 0;
@@ -640,9 +498,6 @@ static int qpnp_tm_probe(struct platform_device *pdev)
	else
		tz_ops = &qpnp_thermal_zone_ops_no_adc;

	chip->allow_software_override
		= of_property_read_bool(node, "qcom,allow-override");

	default_temperature = DEFAULT_NO_ADC_TEMP;
	rc = of_property_read_u32(node, "qcom,default-temp",
					&default_temperature);
@@ -686,18 +541,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
		}
	}

	/* Start in HW control; switch to SW control when user changes mode. */
	chip->mode = THERMAL_DEVICE_DISABLED;
	rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
	if (rc) {
		dev_err(&pdev->dev,
			"%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
			__func__, rc);
		goto err_cancel_work;
	}

	chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, 0, chip,
			tz_ops, NULL, 0, 0);
	chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
							tz_ops);
	if (chip->tz_dev == NULL) {
		dev_err(&pdev->dev,
			"%s: thermal_zone_device_register() failed.\n",
@@ -717,7 +562,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
	return 0;

err_free_tz:
	thermal_zone_device_unregister(chip->tz_dev);
	thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev);
err_cancel_work:
	cancel_delayed_work_sync(&chip->irq_work);
	kfree(chip->tm_name);
@@ -731,10 +576,9 @@ static int qpnp_tm_remove(struct platform_device *pdev)
{
	struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);

	thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev);
	dev_set_drvdata(&pdev->dev, NULL);
	thermal_zone_device_unregister(chip->tz_dev);
	kfree(chip->tm_name);
	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
	free_irq(chip->irq, chip);
	cancel_delayed_work_sync(&chip->irq_work);
	kfree(chip);
@@ -742,38 +586,6 @@ static int qpnp_tm_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_PM
static int qpnp_tm_suspend(struct device *dev)
{
	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);

	/* Clear override bits in suspend to allow hardware control */
	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);

	return 0;
}

static int qpnp_tm_resume(struct device *dev)
{
	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);

	/* Override hardware actions so software can control */
	if (chip->mode == THERMAL_DEVICE_ENABLED)
		qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);

	return 0;
}

static const struct dev_pm_ops qpnp_tm_pm_ops = {
	.suspend = qpnp_tm_suspend,
	.resume = qpnp_tm_resume,
};

#define QPNP_TM_PM_OPS	(&qpnp_tm_pm_ops)
#else
#define QPNP_TM_PM_OPS	NULL
#endif

static const struct of_device_id qpnp_tm_match_table[] = {
	{ .compatible = QPNP_TM_DRIVER_NAME, },
	{}
@@ -789,7 +601,6 @@ static struct platform_driver qpnp_tm_driver = {
		.name		= QPNP_TM_DRIVER_NAME,
		.of_match_table	= qpnp_tm_match_table,
		.owner		= THIS_MODULE,
		.pm		= QPNP_TM_PM_OPS,
	},
	.probe	  = qpnp_tm_probe,
	.remove	  = qpnp_tm_remove,