ipq806x: use newer tsens patch
Use improved tsens patch proposed upstream. Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
This commit is contained in:
		 Ansuel Smith
					Ansuel Smith
				
			
				
					committed by
					
						 Petr Štetiar
						Petr Štetiar
					
				
			
			
				
	
			
			
			 Petr Štetiar
						Petr Štetiar
					
				
			
						parent
						
							d53be2a2e9
						
					
				
				
					commit
					96f10c9d7a
				
			| @@ -1,616 +0,0 @@ | |||||||
| From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Rajith Cherian <rajith@codeaurora.org> |  | ||||||
| Date: Tue, 14 Feb 2017 18:30:43 +0530 |  | ||||||
| Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064 |  | ||||||
|  |  | ||||||
| Add TSENS driver template to support IPQ8064. |  | ||||||
| This is a base file copied from tsens-8960.c |  | ||||||
|  |  | ||||||
| Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7 |  | ||||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |  | ||||||
|  |  | ||||||
| ipq8064: tsens: TSENS driver support for IPQ8064 |  | ||||||
|  |  | ||||||
| Support for IPQ8064 tsens driver. The driver works |  | ||||||
| with the thermal framework. The driver overrides the |  | ||||||
| following fucntionalities: |  | ||||||
|  |  | ||||||
| 1. Get current temperature. |  | ||||||
| 2. Get/Set trip temperatures. |  | ||||||
| 3. Enabled/Disable trip points. |  | ||||||
| 4. ISR for threshold generated interrupt. |  | ||||||
| 5. Notify userspace when trip points are hit. |  | ||||||
|  |  | ||||||
| Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918 |  | ||||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| --- a/drivers/thermal/qcom/Makefile |  | ||||||
| +++ b/drivers/thermal/qcom/Makefile |  | ||||||
| @@ -2,5 +2,5 @@ |  | ||||||
|  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 |  | ||||||
| +				   tsens-8960.o tsens-v2.o tsens-v1.o tsens-ipq8064.o |  | ||||||
|  obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM)	+= qcom-spmi-temp-alarm.o |  | ||||||
| --- /dev/null |  | ||||||
| +++ b/drivers/thermal/qcom/tsens-ipq8064.c |  | ||||||
| @@ -0,0 +1,551 @@ |  | ||||||
| +/* |  | ||||||
| + * Copyright (c) 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 |  | ||||||
| + * only version 2 as published by the Free Software Foundation. |  | ||||||
| + * |  | ||||||
| + * This program is distributed in the hope that it will be useful, |  | ||||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| + * GNU General Public License for more details. |  | ||||||
| + * |  | ||||||
| + */ |  | ||||||
| + |  | ||||||
| +#include <linux/platform_device.h> |  | ||||||
| +#include <linux/delay.h> |  | ||||||
| +#include <linux/bitops.h> |  | ||||||
| +#include <linux/regmap.h> |  | ||||||
| +#include <linux/thermal.h> |  | ||||||
| +#include <linux/nvmem-consumer.h> |  | ||||||
| +#include <linux/io.h> |  | ||||||
| +#include <linux/interrupt.h> |  | ||||||
| +#include "tsens.h" |  | ||||||
| + |  | ||||||
| +#define CAL_MDEGC		30000 |  | ||||||
| + |  | ||||||
| +#define CONFIG_ADDR		0x3640 |  | ||||||
| +/* CONFIG_ADDR bitmasks */ |  | ||||||
| +#define CONFIG			0x9b |  | ||||||
| +#define CONFIG_MASK		0xf |  | ||||||
| +#define CONFIG_SHIFT		0 |  | ||||||
| + |  | ||||||
| +#define STATUS_CNTL_8064	0x3660 |  | ||||||
| +#define CNTL_ADDR		0x3620 |  | ||||||
| +/* CNTL_ADDR bitmasks */ |  | ||||||
| +#define EN			BIT(0) |  | ||||||
| +#define SW_RST			BIT(1) |  | ||||||
| +#define SENSOR0_EN		BIT(3) |  | ||||||
| +#define SLP_CLK_ENA		BIT(26) |  | ||||||
| +#define MEASURE_PERIOD		1 |  | ||||||
| +#define SENSOR0_SHIFT		3 |  | ||||||
| + |  | ||||||
| +/* INT_STATUS_ADDR bitmasks */ |  | ||||||
| +#define MIN_STATUS_MASK		BIT(0) |  | ||||||
| +#define LOWER_STATUS_CLR	BIT(1) |  | ||||||
| +#define UPPER_STATUS_CLR	BIT(2) |  | ||||||
| +#define MAX_STATUS_MASK		BIT(3) |  | ||||||
| + |  | ||||||
| +#define THRESHOLD_ADDR		0x3624 |  | ||||||
| +/* THRESHOLD_ADDR bitmasks */ |  | ||||||
| +#define THRESHOLD_MAX_CODE		0x20000 |  | ||||||
| +#define THRESHOLD_MIN_CODE		0 |  | ||||||
| +#define THRESHOLD_MAX_LIMIT_SHIFT	24 |  | ||||||
| +#define THRESHOLD_MIN_LIMIT_SHIFT	16 |  | ||||||
| +#define THRESHOLD_UPPER_LIMIT_SHIFT	8 |  | ||||||
| +#define THRESHOLD_LOWER_LIMIT_SHIFT	0 |  | ||||||
| +#define THRESHOLD_MAX_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ |  | ||||||
| +						THRESHOLD_MAX_LIMIT_SHIFT) |  | ||||||
| +#define THRESHOLD_MIN_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ |  | ||||||
| +						THRESHOLD_MIN_LIMIT_SHIFT) |  | ||||||
| +#define THRESHOLD_UPPER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ |  | ||||||
| +						THRESHOLD_UPPER_LIMIT_SHIFT) |  | ||||||
| +#define THRESHOLD_LOWER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ |  | ||||||
| +						THRESHOLD_LOWER_LIMIT_SHIFT) |  | ||||||
| + |  | ||||||
| +/* Initial temperature threshold values */ |  | ||||||
| +#define LOWER_LIMIT_TH		0x9d /* 95C */ |  | ||||||
| +#define UPPER_LIMIT_TH		0xa6 /* 105C */ |  | ||||||
| +#define MIN_LIMIT_TH		0x0 |  | ||||||
| +#define MAX_LIMIT_TH		0xff |  | ||||||
| + |  | ||||||
| +#define S0_STATUS_ADDR		0x3628 |  | ||||||
| +#define STATUS_ADDR_OFFSET	2 |  | ||||||
| +#define SENSOR_STATUS_SIZE	4 |  | ||||||
| +#define INT_STATUS_ADDR		0x363c |  | ||||||
| +#define TRDY_MASK		BIT(7) |  | ||||||
| +#define TIMEOUT_US		100 |  | ||||||
| + |  | ||||||
| +#define TSENS_EN		BIT(0) |  | ||||||
| +#define TSENS_SW_RST		BIT(1) |  | ||||||
| +#define TSENS_ADC_CLK_SEL	BIT(2) |  | ||||||
| +#define SENSOR0_EN		BIT(3) |  | ||||||
| +#define SENSOR1_EN		BIT(4) |  | ||||||
| +#define SENSOR2_EN		BIT(5) |  | ||||||
| +#define SENSOR3_EN		BIT(6) |  | ||||||
| +#define SENSOR4_EN		BIT(7) |  | ||||||
| +#define SENSORS_EN		(SENSOR0_EN | SENSOR1_EN | \ |  | ||||||
| +				SENSOR2_EN | SENSOR3_EN | SENSOR4_EN) |  | ||||||
| +#define TSENS_8064_SENSOR5_EN				BIT(8) |  | ||||||
| +#define TSENS_8064_SENSOR6_EN				BIT(9) |  | ||||||
| +#define TSENS_8064_SENSOR7_EN				BIT(10) |  | ||||||
| +#define TSENS_8064_SENSOR8_EN				BIT(11) |  | ||||||
| +#define TSENS_8064_SENSOR9_EN				BIT(12) |  | ||||||
| +#define TSENS_8064_SENSOR10_EN				BIT(13) |  | ||||||
| +#define TSENS_8064_SENSORS_EN				(SENSORS_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR5_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR6_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR7_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR8_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR9_EN | \ |  | ||||||
| +						TSENS_8064_SENSOR10_EN) |  | ||||||
| + |  | ||||||
| +#define TSENS_8064_SEQ_SENSORS	5 |  | ||||||
| +#define TSENS_8064_S4_S5_OFFSET	40 |  | ||||||
| +#define TSENS_FACTOR		1 |  | ||||||
| + |  | ||||||
| +/* Trips: from very hot to very cold */ |  | ||||||
| +enum tsens_trip_type { |  | ||||||
| +	TSENS_TRIP_STAGE3 = 0, |  | ||||||
| +	TSENS_TRIP_STAGE2, |  | ||||||
| +	TSENS_TRIP_STAGE1, |  | ||||||
| +	TSENS_TRIP_STAGE0, |  | ||||||
| +	TSENS_TRIP_NUM, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +u32 tsens_8064_slope[] = { |  | ||||||
| +			1176, 1176, 1154, 1176, |  | ||||||
| +			1111, 1132, 1132, 1199, |  | ||||||
| +			1132, 1199, 1132 |  | ||||||
| +			}; |  | ||||||
| + |  | ||||||
| +/* Temperature on y axis and ADC-code on x-axis */ |  | ||||||
| +static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s) |  | ||||||
| +{ |  | ||||||
| +	int degcbeforefactor, degc; |  | ||||||
| + |  | ||||||
| +	degcbeforefactor = (adc_code * s->slope) + s->offset; |  | ||||||
| + |  | ||||||
| +	if (degcbeforefactor == 0) |  | ||||||
| +		degc = degcbeforefactor; |  | ||||||
| +	else if (degcbeforefactor > 0) |  | ||||||
| +		degc = (degcbeforefactor + TSENS_FACTOR/2) |  | ||||||
| +			/ TSENS_FACTOR; |  | ||||||
| +	else |  | ||||||
| +		degc = (degcbeforefactor - TSENS_FACTOR/2) |  | ||||||
| +			/ TSENS_FACTOR; |  | ||||||
| + |  | ||||||
| +	return degc; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int degC_to_code(int degC, const struct tsens_sensor *s) |  | ||||||
| +{ |  | ||||||
| +	int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2)) |  | ||||||
| +			/ s->slope; |  | ||||||
| + |  | ||||||
| +	if (code > THRESHOLD_MAX_CODE) |  | ||||||
| +		code = THRESHOLD_MAX_CODE; |  | ||||||
| +	else if (code < THRESHOLD_MIN_CODE) |  | ||||||
| +		code = THRESHOLD_MIN_CODE; |  | ||||||
| +	return code; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int suspend_ipq8064(struct tsens_priv *priv) |  | ||||||
| +{ |  | ||||||
| +	int ret; |  | ||||||
| +	unsigned int mask; |  | ||||||
| +	struct regmap *map = priv->tm_map; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	mask = SLP_CLK_ENA | EN; |  | ||||||
| + |  | ||||||
| +	ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int resume_ipq8064(struct tsens_priv *priv) |  | ||||||
| +{ |  | ||||||
| +	int ret; |  | ||||||
| +	struct regmap *map = priv->tm_map; |  | ||||||
| + |  | ||||||
| +	ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static void notify_uspace_tsens_fn(struct work_struct *work) |  | ||||||
| +{ |  | ||||||
| +	struct tsens_sensor *s = container_of(work, struct tsens_sensor, |  | ||||||
| +								notify_work); |  | ||||||
| + |  | ||||||
| +	sysfs_notify(&s->tzd->device.kobj, NULL, "type"); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static void tsens_scheduler_fn(struct work_struct *work) |  | ||||||
| +{ |  | ||||||
| +	struct tsens_priv *priv = container_of(work, struct tsens_priv, |  | ||||||
| +					tsens_work); |  | ||||||
| +	unsigned int threshold, threshold_low, code, reg, sensor, mask; |  | ||||||
| +	unsigned int sensor_addr; |  | ||||||
| +	bool upper_th_x, lower_th_x; |  | ||||||
| +	int adc_code, ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®); |  | ||||||
| +	if (ret) |  | ||||||
| +		return; |  | ||||||
| +	reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR; |  | ||||||
| +	ret = regmap_write(priv->tm_map, STATUS_CNTL_8064, reg); |  | ||||||
| +	if (ret) |  | ||||||
| +		return; |  | ||||||
| + |  | ||||||
| +	mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR); |  | ||||||
| +	ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold); |  | ||||||
| +	if (ret) |  | ||||||
| +		return; |  | ||||||
| +	threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) |  | ||||||
| +				>> THRESHOLD_LOWER_LIMIT_SHIFT; |  | ||||||
| +	threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) |  | ||||||
| +				>> THRESHOLD_UPPER_LIMIT_SHIFT; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®); |  | ||||||
| +	if (ret) |  | ||||||
| +		return; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor); |  | ||||||
| +	if (ret) |  | ||||||
| +		return; |  | ||||||
| +	sensor &= (uint32_t) TSENS_8064_SENSORS_EN; |  | ||||||
| +	sensor >>= SENSOR0_SHIFT; |  | ||||||
| + |  | ||||||
| +	/* Constraint: There is only 1 interrupt control register for all |  | ||||||
| +	 * 11 temperature sensor. So monitoring more than 1 sensor based |  | ||||||
| +	 * on interrupts will yield inconsistent result. To overcome this |  | ||||||
| +	 * issue we will monitor only sensor 0 which is the master sensor. |  | ||||||
| +	 */ |  | ||||||
| + |  | ||||||
| +	/* Skip if the sensor is disabled */ |  | ||||||
| +	if (sensor & 1) { |  | ||||||
| +		ret = regmap_read(priv->tm_map, priv->sensor[0].status, &code); |  | ||||||
| +		if (ret) |  | ||||||
| +			return; |  | ||||||
| +		upper_th_x = code >= threshold; |  | ||||||
| +		lower_th_x = code <= threshold_low; |  | ||||||
| +		if (upper_th_x) |  | ||||||
| +			mask |= UPPER_STATUS_CLR; |  | ||||||
| +		if (lower_th_x) |  | ||||||
| +			mask |= LOWER_STATUS_CLR; |  | ||||||
| +		if (upper_th_x || lower_th_x) { |  | ||||||
| +			/* Notify user space */ |  | ||||||
| +			schedule_work(&priv->sensor[0].notify_work); |  | ||||||
| +			regmap_read(priv->tm_map, sensor_addr, &adc_code); |  | ||||||
| +			pr_debug("Trigger (%d degrees) for sensor %d\n", |  | ||||||
| +				code_to_degC(adc_code, &priv->sensor[0]), 0); |  | ||||||
| +		} |  | ||||||
| +	} |  | ||||||
| +	regmap_write(priv->tm_map, STATUS_CNTL_8064, reg & mask); |  | ||||||
| + |  | ||||||
| +	/* force memory to sync */ |  | ||||||
| +	mb(); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static irqreturn_t tsens_isr(int irq, void *data) |  | ||||||
| +{ |  | ||||||
| +	struct tsens_priv *priv = data; |  | ||||||
| + |  | ||||||
| +	schedule_work(&priv->tsens_work); |  | ||||||
| +	return IRQ_HANDLED; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static void hw_init(struct tsens_priv *priv) |  | ||||||
| +{ |  | ||||||
| +	int ret; |  | ||||||
| +	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; |  | ||||||
| +	unsigned int reg_status_cntl = 0; |  | ||||||
| + |  | ||||||
| +	regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); |  | ||||||
| +	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST); |  | ||||||
| + |  | ||||||
| +	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |  | ||||||
| +		| (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT); |  | ||||||
| +	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); |  | ||||||
| +	regmap_read(priv->tm_map, STATUS_CNTL_8064, ®_status_cntl); |  | ||||||
| +	reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |  | ||||||
| +			| MIN_STATUS_MASK | MAX_STATUS_MASK; |  | ||||||
| +	regmap_write(priv->tm_map, STATUS_CNTL_8064, reg_status_cntl); |  | ||||||
| +	reg_cntl |= TSENS_EN; |  | ||||||
| +	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); |  | ||||||
| + |  | ||||||
| +	regmap_read(priv->tm_map, CONFIG_ADDR, ®_cfg); |  | ||||||
| +	reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT); |  | ||||||
| +	regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg); |  | ||||||
| + |  | ||||||
| +	reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT) |  | ||||||
| +		| (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT) |  | ||||||
| +		| (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |  | ||||||
| +		| (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT); |  | ||||||
| + |  | ||||||
| +	regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr); |  | ||||||
| + |  | ||||||
| +	ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr, |  | ||||||
| +			IRQF_TRIGGER_RISING, "tsens_interrupt", priv); |  | ||||||
| +	if (ret < 0) { |  | ||||||
| +		pr_err("%s: request_irq FAIL: %d\n", __func__, ret); |  | ||||||
| +		return; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	INIT_WORK(&priv->tsens_work, tsens_scheduler_fn); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int init_ipq8064(struct tsens_priv *priv) |  | ||||||
| +{ |  | ||||||
| +	int ret, i; |  | ||||||
| +	u32 reg_cntl, offset = 0; |  | ||||||
| + |  | ||||||
| +	init_common(priv); |  | ||||||
| +	if (!priv->tm_map) |  | ||||||
| +		return -ENODEV; |  | ||||||
| + |  | ||||||
| +	/* |  | ||||||
| +	 * The status registers for each sensor are discontiguous |  | ||||||
| +	 * because some SoCs have 5 sensors while others have more |  | ||||||
| +	 * but the control registers stay in the same place, i.e |  | ||||||
| +	 * directly after the first 5 status registers. |  | ||||||
| +	 */ |  | ||||||
| +	for (i = 0; i < priv->num_sensors; i++) { |  | ||||||
| +		if (i >= TSENS_8064_SEQ_SENSORS) |  | ||||||
| +			offset = TSENS_8064_S4_S5_OFFSET; |  | ||||||
| + |  | ||||||
| +		priv->sensor[i].status = S0_STATUS_ADDR + offset |  | ||||||
| +					+ (i << STATUS_ADDR_OFFSET); |  | ||||||
| +		priv->sensor[i].slope = tsens_8064_slope[i]; |  | ||||||
| +		INIT_WORK(&priv->sensor[i].notify_work, |  | ||||||
| +						notify_uspace_tsens_fn); |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	reg_cntl = SW_RST; |  | ||||||
| +	ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); |  | ||||||
| +	reg_cntl &= ~SW_RST; |  | ||||||
| +	ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, |  | ||||||
| +					 CONFIG_MASK, CONFIG); |  | ||||||
| + |  | ||||||
| +	reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; |  | ||||||
| +	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	reg_cntl |= EN; |  | ||||||
| +	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int calibrate_ipq8064(struct tsens_priv *priv) |  | ||||||
| +{ |  | ||||||
| +	int i; |  | ||||||
| +	char *data, *data_backup; |  | ||||||
| + |  | ||||||
| +	ssize_t num_read = priv->num_sensors; |  | ||||||
| +	struct tsens_sensor *s = priv->sensor; |  | ||||||
| + |  | ||||||
| +	data = qfprom_read(priv->dev, "calib"); |  | ||||||
| +	if (IS_ERR(data)) { |  | ||||||
| +		pr_err("Calibration not found.\n"); |  | ||||||
| +		return PTR_ERR(data); |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	data_backup = qfprom_read(priv->dev, "calib_backup"); |  | ||||||
| +	if (IS_ERR(data_backup)) { |  | ||||||
| +		pr_err("Backup calibration not found.\n"); |  | ||||||
| +		return PTR_ERR(data_backup); |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	for (i = 0; i < num_read; i++) { |  | ||||||
| +		s[i].calib_data = readb_relaxed(data + i); |  | ||||||
| +		s[i].calib_data_backup = readb_relaxed(data_backup + i); |  | ||||||
| + |  | ||||||
| +		if (s[i].calib_data_backup) |  | ||||||
| +			s[i].calib_data = s[i].calib_data_backup; |  | ||||||
| +		if (!s[i].calib_data) { |  | ||||||
| +			pr_err("QFPROM TSENS calibration data not present\n"); |  | ||||||
| +			return -ENODEV; |  | ||||||
| +		} |  | ||||||
| +		s[i].slope = tsens_8064_slope[i]; |  | ||||||
| +		s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	hw_init(priv); |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int get_temp_ipq8064(struct tsens_priv *priv, int id, int *temp) |  | ||||||
| +{ |  | ||||||
| +	int ret; |  | ||||||
| +	u32 code, trdy; |  | ||||||
| +	const struct tsens_sensor *s = &priv->sensor[id]; |  | ||||||
| +	unsigned long timeout; |  | ||||||
| + |  | ||||||
| +	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); |  | ||||||
| +	do { |  | ||||||
| +		ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); |  | ||||||
| +		if (ret) |  | ||||||
| +			return ret; |  | ||||||
| +		if (!(trdy & TRDY_MASK)) |  | ||||||
| +			continue; |  | ||||||
| +		ret = regmap_read(priv->tm_map, s->status, &code); |  | ||||||
| +		if (ret) |  | ||||||
| +			return ret; |  | ||||||
| +		*temp = code_to_degC(code, s); |  | ||||||
| +		return 0; |  | ||||||
| +	} while (time_before(jiffies, timeout)); |  | ||||||
| + |  | ||||||
| +	return -ETIMEDOUT; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int set_trip_temp_ipq8064(void *data, int trip, int temp) |  | ||||||
| +{ |  | ||||||
| +	unsigned int reg_th, reg_cntl; |  | ||||||
| +	int ret, code, code_chk, hi_code, lo_code; |  | ||||||
| +	const struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
| + |  | ||||||
| +	code_chk = code = degC_to_code(temp, s); |  | ||||||
| + |  | ||||||
| +	if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE) |  | ||||||
| +		return -EINVAL; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®_cntl); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, ®_th); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK) |  | ||||||
| +			>> THRESHOLD_UPPER_LIMIT_SHIFT; |  | ||||||
| +	lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK) |  | ||||||
| +			>> THRESHOLD_LOWER_LIMIT_SHIFT; |  | ||||||
| + |  | ||||||
| +	switch (trip) { |  | ||||||
| +	case TSENS_TRIP_STAGE3: |  | ||||||
| +		code <<= THRESHOLD_MAX_LIMIT_SHIFT; |  | ||||||
| +		reg_th &= ~THRESHOLD_MAX_LIMIT_MASK; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE2: |  | ||||||
| +		if (code_chk <= lo_code) |  | ||||||
| +			return -EINVAL; |  | ||||||
| +		code <<= THRESHOLD_UPPER_LIMIT_SHIFT; |  | ||||||
| +		reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE1: |  | ||||||
| +		if (code_chk >= hi_code) |  | ||||||
| +			return -EINVAL; |  | ||||||
| +		code <<= THRESHOLD_LOWER_LIMIT_SHIFT; |  | ||||||
| +		reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE0: |  | ||||||
| +		code <<= THRESHOLD_MIN_LIMIT_SHIFT; |  | ||||||
| +		reg_th &= ~THRESHOLD_MIN_LIMIT_MASK; |  | ||||||
| +		break; |  | ||||||
| +	default: |  | ||||||
| +		return -EINVAL; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	ret = regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_th | code); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int set_trip_activate_ipq8064(void *data, int trip, |  | ||||||
| +					enum thermal_trip_activation_mode mode) |  | ||||||
| +{ |  | ||||||
| +	unsigned int reg_cntl, mask, val; |  | ||||||
| +	const struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
| +	int ret; |  | ||||||
| + |  | ||||||
| +	if (!priv || trip < 0) |  | ||||||
| +		return -EINVAL; |  | ||||||
| + |  | ||||||
| +	ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®_cntl); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	switch (trip) { |  | ||||||
| +	case TSENS_TRIP_STAGE3: |  | ||||||
| +		mask = MAX_STATUS_MASK; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE2: |  | ||||||
| +		mask = UPPER_STATUS_CLR; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE1: |  | ||||||
| +		mask = LOWER_STATUS_CLR; |  | ||||||
| +		break; |  | ||||||
| +	case TSENS_TRIP_STAGE0: |  | ||||||
| +		mask = MIN_STATUS_MASK; |  | ||||||
| +		break; |  | ||||||
| +	default: |  | ||||||
| +		return -EINVAL; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) |  | ||||||
| +		val = reg_cntl | mask; |  | ||||||
| +	else |  | ||||||
| +		val = reg_cntl & ~mask; |  | ||||||
| + |  | ||||||
| +	ret = regmap_write(priv->tm_map, STATUS_CNTL_8064, val); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	/* force memory to sync */ |  | ||||||
| +	mb(); |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +const struct tsens_ops ops_ipq8064 = { |  | ||||||
| +	.init		= init_ipq8064, |  | ||||||
| +	.calibrate	= calibrate_ipq8064, |  | ||||||
| +	.get_temp	= get_temp_ipq8064, |  | ||||||
| +	.suspend	= suspend_ipq8064, |  | ||||||
| +	.resume		= resume_ipq8064, |  | ||||||
| +	.set_trip_temp	= set_trip_temp_ipq8064, |  | ||||||
| +	.set_trip_activate = set_trip_activate_ipq8064, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +const struct tsens_plat_data data_ipq8064 = { |  | ||||||
| +	.num_sensors	= 11, |  | ||||||
| +	.ops		= &ops_ipq8064, |  | ||||||
| +}; |  | ||||||
| --- a/drivers/thermal/qcom/tsens.c |  | ||||||
| +++ b/drivers/thermal/qcom/tsens.c |  | ||||||
| @@ -69,8 +69,11 @@ static const struct of_device_id tsens_t |  | ||||||
|  	}, { |  | ||||||
|  		.compatible = "qcom,tsens-v2", |  | ||||||
|  		.data = &data_tsens_v2, |  | ||||||
| +	}, { |  | ||||||
| +		.compatible = "qcom,ipq8064-tsens", |  | ||||||
| +		.data = &data_ipq8064, |  | ||||||
|  	}, |  | ||||||
| -	{} |  | ||||||
| + 	{} |  | ||||||
|  }; |  | ||||||
|  MODULE_DEVICE_TABLE(of, tsens_table); |  | ||||||
|   |  | ||||||
| --- a/drivers/thermal/qcom/tsens.h |  | ||||||
| +++ b/drivers/thermal/qcom/tsens.h |  | ||||||
| @@ -324,7 +324,7 @@ extern const struct tsens_plat_data data |  | ||||||
|  extern const struct tsens_plat_data data_8916, data_8974; |  | ||||||
|   |  | ||||||
|  /* TSENS v1 targets */ |  | ||||||
| -extern const struct tsens_plat_data data_tsens_v1; |  | ||||||
| +extern const struct tsens_plat_data data_tsens_v1, data_ipq8064; |  | ||||||
|   |  | ||||||
|  /* TSENS v2 targets */ |  | ||||||
|  extern const struct tsens_plat_data data_8996, data_tsens_v2; |  | ||||||
| @@ -1,437 +0,0 @@ | |||||||
| From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Rajith Cherian <rajith@codeaurora.org> |  | ||||||
| Date: Wed, 1 Feb 2017 19:00:26 +0530 |  | ||||||
| Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts |  | ||||||
|  |  | ||||||
| Provide support for adding configurable high and |  | ||||||
| configurable low trip temperatures. An interrupts is |  | ||||||
| also triggerred when these trip points are hit. The |  | ||||||
| interrupts can be activated or deactivated from sysfs. |  | ||||||
| This functionality is made available only if |  | ||||||
| CONFIG_THERMAL_WRITABLE_TRIPS is defined. |  | ||||||
|  |  | ||||||
| Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934 |  | ||||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |  | ||||||
| --- |  | ||||||
|  .../devicetree/bindings/thermal/qcom-tsens.txt     |  4 ++ |  | ||||||
|  drivers/thermal/of-thermal.c                       | 63 ++++++++++++++++++---- |  | ||||||
|  drivers/thermal/qcom/tsens.c                       | 43 ++++++++++++--- |  | ||||||
|  drivers/thermal/qcom/tsens.h                       | 11 ++++ |  | ||||||
|  drivers/thermal/thermal_core.c                     | 44 ++++++++++++++- |  | ||||||
|  include/linux/thermal.h                            | 14 +++++ |  | ||||||
|  6 files changed, 162 insertions(+), 17 deletions(-) |  | ||||||
|  |  | ||||||
| --- a/drivers/thermal/of-thermal.c |  | ||||||
| +++ b/drivers/thermal/of-thermal.c |  | ||||||
| @@ -91,7 +91,7 @@ static int of_thermal_get_temp(struct th |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (!data->ops->get_temp) |  | ||||||
| +	if (!data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EINVAL; |  | ||||||
|   |  | ||||||
|  	return data->ops->get_temp(data->sensor_data, temp); |  | ||||||
| @@ -102,7 +102,8 @@ static int of_thermal_set_trips(struct t |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (!data->ops || !data->ops->set_trips) |  | ||||||
| +	if (!data->ops || !data->ops->set_trips |  | ||||||
| +			|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EINVAL; |  | ||||||
|   |  | ||||||
|  	return data->ops->set_trips(data->sensor_data, low, high); |  | ||||||
| @@ -188,6 +189,9 @@ static int of_thermal_set_emul_temp(stru |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| +	if (data->mode == THERMAL_DEVICE_DISABLED) |  | ||||||
| +		return -EINVAL; |  | ||||||
| + |  | ||||||
|  	return data->ops->set_emul_temp(data->sensor_data, temp); |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| @@ -196,7 +200,7 @@ static int of_thermal_get_trend(struct t |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (!data->ops->get_trend) |  | ||||||
| +	if (!data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EINVAL; |  | ||||||
|   |  | ||||||
|  	return data->ops->get_trend(data->sensor_data, trip, trend); |  | ||||||
| @@ -297,7 +301,9 @@ static int of_thermal_set_mode(struct th |  | ||||||
|  	mutex_unlock(&tz->lock); |  | ||||||
|   |  | ||||||
|  	data->mode = mode; |  | ||||||
| -	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |  | ||||||
| + |  | ||||||
| +	if (mode == THERMAL_DEVICE_ENABLED) |  | ||||||
| +		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |  | ||||||
|   |  | ||||||
|  	return 0; |  | ||||||
|  } |  | ||||||
| @@ -307,7 +313,8 @@ static int of_thermal_get_trip_type(stru |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (trip >= data->ntrips || trip < 0) |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EDOM; |  | ||||||
|   |  | ||||||
|  	*type = data->trips[trip].type; |  | ||||||
| @@ -315,12 +322,39 @@ static int of_thermal_get_trip_type(stru |  | ||||||
|  	return 0; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +static int of_thermal_activate_trip_type(struct thermal_zone_device *tz, |  | ||||||
| +			int trip, enum thermal_trip_activation_mode mode) |  | ||||||
| +{ |  | ||||||
| +	struct __thermal_zone *data = tz->devdata; |  | ||||||
| + |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
| +		return -EDOM; |  | ||||||
| + |  | ||||||
| +	/* |  | ||||||
| +	 * The configurable_hi and configurable_lo trip points can be |  | ||||||
| +	 * activated and deactivated. |  | ||||||
| +	 */ |  | ||||||
| + |  | ||||||
| +	if (data->ops->set_trip_activate) { |  | ||||||
| +		int ret; |  | ||||||
| + |  | ||||||
| +		ret = data->ops->set_trip_activate(data->sensor_data, |  | ||||||
| +								trip, mode); |  | ||||||
| +		if (ret) |  | ||||||
| +			return ret; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
|  static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, |  | ||||||
|  				    int *temp) |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (trip >= data->ntrips || trip < 0) |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EDOM; |  | ||||||
|   |  | ||||||
|  	*temp = data->trips[trip].temperature; |  | ||||||
| @@ -333,7 +367,8 @@ static int of_thermal_set_trip_temp(stru |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (trip >= data->ntrips || trip < 0) |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EDOM; |  | ||||||
|   |  | ||||||
|  	if (data->ops->set_trip_temp) { |  | ||||||
| @@ -355,7 +390,8 @@ static int of_thermal_get_trip_hyst(stru |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (trip >= data->ntrips || trip < 0) |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EDOM; |  | ||||||
|   |  | ||||||
|  	*hyst = data->trips[trip].hysteresis; |  | ||||||
| @@ -368,7 +404,8 @@ static int of_thermal_set_trip_hyst(stru |  | ||||||
|  { |  | ||||||
|  	struct __thermal_zone *data = tz->devdata; |  | ||||||
|   |  | ||||||
| -	if (trip >= data->ntrips || trip < 0) |  | ||||||
| +	if (trip >= data->ntrips || trip < 0 |  | ||||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) |  | ||||||
|  		return -EDOM; |  | ||||||
|   |  | ||||||
|  	/* thermal framework should take care of data->mask & (1 << trip) */ |  | ||||||
| @@ -443,6 +480,9 @@ thermal_zone_of_add_sensor(struct device |  | ||||||
|  	if (ops->set_emul_temp) |  | ||||||
|  		tzd->ops->set_emul_temp = of_thermal_set_emul_temp; |  | ||||||
|   |  | ||||||
| +	if (ops->set_trip_activate) |  | ||||||
| +		tzd->ops->set_trip_activate = of_thermal_activate_trip_type; |  | ||||||
| + |  | ||||||
|  	mutex_unlock(&tzd->lock); |  | ||||||
|   |  | ||||||
|  	return tzd; |  | ||||||
| @@ -762,7 +802,10 @@ static const char * const trip_types[] = |  | ||||||
|  	[THERMAL_TRIP_ACTIVE]	= "active", |  | ||||||
|  	[THERMAL_TRIP_PASSIVE]	= "passive", |  | ||||||
|  	[THERMAL_TRIP_HOT]	= "hot", |  | ||||||
| -	[THERMAL_TRIP_CRITICAL]	= "critical", |  | ||||||
| +	[THERMAL_TRIP_CRITICAL]	= "critical_high", |  | ||||||
| +	[THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi", |  | ||||||
| +	[THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo", |  | ||||||
| +	[THERMAL_TRIP_CRITICAL_LOW] = "critical_low", |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  /** |  | ||||||
| --- a/drivers/thermal/qcom/tsens.c |  | ||||||
| +++ b/drivers/thermal/qcom/tsens.c |  | ||||||
| @@ -22,7 +22,7 @@ static int tsens_get_temp(void *data, in |  | ||||||
|   |  | ||||||
|  static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) |  | ||||||
|  { |  | ||||||
| -	const struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_sensor *s = data; |  | ||||||
|  	struct tsens_priv *priv = s->priv; |  | ||||||
|   |  | ||||||
|  	if (priv->ops->get_trend) |  | ||||||
| @@ -31,9 +31,10 @@ static int tsens_get_trend(void *data, i |  | ||||||
|  	return -ENOTSUPP; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -static int  __maybe_unused tsens_suspend(struct device *dev) |  | ||||||
| +static int  __maybe_unused tsens_suspend(void *data) |  | ||||||
|  { |  | ||||||
| -	struct tsens_priv *priv = dev_get_drvdata(dev); |  | ||||||
| +	struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
|   |  | ||||||
|  	if (priv->ops && priv->ops->suspend) |  | ||||||
|  		return priv->ops->suspend(priv); |  | ||||||
| @@ -41,9 +42,10 @@ static int  __maybe_unused tsens_suspend |  | ||||||
|  	return 0; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -static int __maybe_unused tsens_resume(struct device *dev) |  | ||||||
| +static int __maybe_unused tsens_resume(void *data) |  | ||||||
|  { |  | ||||||
| -	struct tsens_priv *priv = dev_get_drvdata(dev); |  | ||||||
| +	struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
|   |  | ||||||
|  	if (priv->ops && priv->ops->resume) |  | ||||||
|  		return priv->ops->resume(priv); |  | ||||||
| @@ -51,6 +53,30 @@ static int __maybe_unused tsens_resume(s |  | ||||||
|  	return 0; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +static int  __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp) |  | ||||||
| +{ |  | ||||||
| +	struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
| + |  | ||||||
| +	if (priv->ops && priv->ops->set_trip_temp) |  | ||||||
| +		return priv->ops->set_trip_temp(s, trip, temp); |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int __maybe_unused tsens_activate_trip_type(void *data, int trip, |  | ||||||
| +					enum thermal_trip_activation_mode mode) |  | ||||||
| +{ |  | ||||||
| +	struct tsens_sensor *s = data; |  | ||||||
| +	struct tsens_priv *priv = s->priv; |  | ||||||
| + |  | ||||||
| +	if (priv->ops && priv->ops->set_trip_activate) |  | ||||||
| +		return priv->ops->set_trip_activate(s, trip, mode); |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| + |  | ||||||
|  static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); |  | ||||||
|   |  | ||||||
|  static const struct of_device_id tsens_table[] = { |  | ||||||
| @@ -80,6 +106,8 @@ MODULE_DEVICE_TABLE(of, tsens_table); |  | ||||||
|  static const struct thermal_zone_of_device_ops tsens_of_ops = { |  | ||||||
|  	.get_temp = tsens_get_temp, |  | ||||||
|  	.get_trend = tsens_get_trend, |  | ||||||
| +	.set_trip_temp = tsens_set_trip_temp, |  | ||||||
| +	.set_trip_activate = tsens_activate_trip_type, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  static int tsens_register(struct tsens_priv *priv) |  | ||||||
| @@ -123,7 +151,7 @@ static int tsens_probe(struct platform_d |  | ||||||
|  	if (id) |  | ||||||
|  		data = id->data; |  | ||||||
|  	else |  | ||||||
| -		data = &data_8960; |  | ||||||
| +		return -EINVAL; |  | ||||||
|   |  | ||||||
|  	num_sensors = data->num_sensors; |  | ||||||
|   |  | ||||||
| @@ -144,6 +172,9 @@ static int tsens_probe(struct platform_d |  | ||||||
|  	priv->dev = dev; |  | ||||||
|  	priv->num_sensors = num_sensors; |  | ||||||
|  	priv->ops = data->ops; |  | ||||||
| + |  | ||||||
| +	priv->tsens_irq = platform_get_irq(pdev, 0); |  | ||||||
| + |  | ||||||
|  	for (i = 0;  i < priv->num_sensors; i++) { |  | ||||||
|  		if (data->hw_ids) |  | ||||||
|  			priv->sensor[i].hw_id = data->hw_ids[i]; |  | ||||||
| --- a/drivers/thermal/qcom/tsens.h |  | ||||||
| +++ b/drivers/thermal/qcom/tsens.h |  | ||||||
| @@ -40,9 +40,12 @@ enum tsens_ver { |  | ||||||
|  struct tsens_sensor { |  | ||||||
|  	struct tsens_priv		*priv; |  | ||||||
|  	struct thermal_zone_device	*tzd; |  | ||||||
| +	struct work_struct		notify_work; |  | ||||||
|  	int				offset; |  | ||||||
|  	unsigned int			id; |  | ||||||
|  	unsigned int			hw_id; |  | ||||||
| +	int				calib_data; |  | ||||||
| +	int				calib_data_backup; |  | ||||||
|  	int				slope; |  | ||||||
|  	u32				status; |  | ||||||
|  }; |  | ||||||
| @@ -57,6 +60,9 @@ struct tsens_sensor { |  | ||||||
|   * @suspend: Function to suspend the tsens device |  | ||||||
|   * @resume: Function to resume the tsens device |  | ||||||
|   * @get_trend: Function to get the thermal/temp trend |  | ||||||
| + * @set_trip_temp: Function to set trip temp |  | ||||||
| + * @get_trip_temp: Function to get trip temp |  | ||||||
| + * @set_trip_activate: Function to activate trip points |  | ||||||
|   */ |  | ||||||
|  struct tsens_ops { |  | ||||||
|  	/* mandatory callbacks */ |  | ||||||
| @@ -69,6 +75,9 @@ struct tsens_ops { |  | ||||||
|  	int (*suspend)(struct tsens_priv *priv); |  | ||||||
|  	int (*resume)(struct tsens_priv *priv); |  | ||||||
|  	int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend); |  | ||||||
| +	int (*set_trip_temp)(void *data, int trip, int temp); |  | ||||||
| +	int (*set_trip_activate)(void *data, int trip, |  | ||||||
| +					enum thermal_trip_activation_mode mode); |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  #define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \ |  | ||||||
| @@ -300,6 +309,7 @@ struct tsens_context { |  | ||||||
|  struct tsens_priv { |  | ||||||
|  	struct device			*dev; |  | ||||||
|  	u32				num_sensors; |  | ||||||
| +	u32				tsens_irq; |  | ||||||
|  	struct regmap			*tm_map; |  | ||||||
|  	struct regmap			*srot_map; |  | ||||||
|  	u32				tm_offset; |  | ||||||
| @@ -308,6 +318,7 @@ struct tsens_priv { |  | ||||||
|  	const struct tsens_features	*feat; |  | ||||||
|  	const struct reg_field		*fields; |  | ||||||
|  	const struct tsens_ops		*ops; |  | ||||||
| +	struct work_struct		tsens_work; |  | ||||||
|  	struct tsens_sensor		sensor[0]; |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| --- a/drivers/thermal/thermal_sysfs.c |  | ||||||
| +++ b/drivers/thermal/thermal_sysfs.c |  | ||||||
| @@ -113,12 +113,48 @@ trip_point_type_show(struct device *dev, |  | ||||||
|  		return sprintf(buf, "passive\n"); |  | ||||||
|  	case THERMAL_TRIP_ACTIVE: |  | ||||||
|  		return sprintf(buf, "active\n"); |  | ||||||
| +	case THERMAL_TRIP_CONFIGURABLE_HI: |  | ||||||
| +		return sprintf(buf, "configurable_hi\n"); |  | ||||||
| +	case THERMAL_TRIP_CONFIGURABLE_LOW: |  | ||||||
| +		return sprintf(buf, "configurable_low\n"); |  | ||||||
| +	case THERMAL_TRIP_CRITICAL_LOW: |  | ||||||
| +		return sprintf(buf, "critical_low\n"); |  | ||||||
|  	default: |  | ||||||
|  		return sprintf(buf, "unknown\n"); |  | ||||||
|  	} |  | ||||||
|  } |  | ||||||
|   |  | ||||||
|  static ssize_t |  | ||||||
| +trip_point_type_activate(struct device *dev, struct device_attribute *attr, |  | ||||||
| +						const char *buf, size_t count) |  | ||||||
| +{ |  | ||||||
| +	struct thermal_zone_device *tz = to_thermal_zone(dev); |  | ||||||
| +	int trip, ret; |  | ||||||
| +	char *enabled = "enabled"; |  | ||||||
| +	char *disabled = "disabled"; |  | ||||||
| + |  | ||||||
| +	if (!tz->ops->set_trip_activate) |  | ||||||
| +		return -EPERM; |  | ||||||
| + |  | ||||||
| +	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) |  | ||||||
| +		return -EINVAL; |  | ||||||
| + |  | ||||||
| +	if (!strncmp(buf, enabled, strlen(enabled))) |  | ||||||
| +		ret = tz->ops->set_trip_activate(tz, trip, |  | ||||||
| +				THERMAL_TRIP_ACTIVATION_ENABLED); |  | ||||||
| +	else if (!strncmp(buf, disabled, strlen(disabled))) |  | ||||||
| +		ret = tz->ops->set_trip_activate(tz, trip, |  | ||||||
| +				THERMAL_TRIP_ACTIVATION_DISABLED); |  | ||||||
| +	else |  | ||||||
| +		ret = -EINVAL; |  | ||||||
| + |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	return count; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static ssize_t |  | ||||||
|  trip_point_temp_store(struct device *dev, struct device_attribute *attr, |  | ||||||
|  		      const char *buf, size_t count) |  | ||||||
|  { |  | ||||||
| @@ -559,6 +595,12 @@ static int create_trip_attrs(struct ther |  | ||||||
|  		tz->trip_type_attrs[indx].attr.show = trip_point_type_show; |  | ||||||
|  		attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; |  | ||||||
|   |  | ||||||
| +		if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) { |  | ||||||
| +			tz->trip_type_attrs[indx].attr.store |  | ||||||
| +						= trip_point_type_activate; |  | ||||||
| +			tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR; |  | ||||||
| +		} |  | ||||||
| + |  | ||||||
|  		/* create trip temp attribute */ |  | ||||||
|  		snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, |  | ||||||
|  			 "trip_point_%d_temp", indx); |  | ||||||
| --- a/include/linux/thermal.h |  | ||||||
| +++ b/include/linux/thermal.h |  | ||||||
| @@ -63,11 +63,19 @@ enum thermal_device_mode { |  | ||||||
|  	THERMAL_DEVICE_ENABLED, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| +enum thermal_trip_activation_mode { |  | ||||||
| +	THERMAL_TRIP_ACTIVATION_DISABLED = 0, |  | ||||||
| +	THERMAL_TRIP_ACTIVATION_ENABLED, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
|  enum thermal_trip_type { |  | ||||||
|  	THERMAL_TRIP_ACTIVE = 0, |  | ||||||
|  	THERMAL_TRIP_PASSIVE, |  | ||||||
|  	THERMAL_TRIP_HOT, |  | ||||||
|  	THERMAL_TRIP_CRITICAL, |  | ||||||
| +	THERMAL_TRIP_CONFIGURABLE_HI, |  | ||||||
| +	THERMAL_TRIP_CONFIGURABLE_LOW, |  | ||||||
| +	THERMAL_TRIP_CRITICAL_LOW, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  enum thermal_trend { |  | ||||||
| @@ -105,6 +113,8 @@ struct thermal_zone_device_ops { |  | ||||||
|  		enum thermal_trip_type *); |  | ||||||
|  	int (*get_trip_temp) (struct thermal_zone_device *, int, int *); |  | ||||||
|  	int (*set_trip_temp) (struct thermal_zone_device *, int, int); |  | ||||||
| +	int (*set_trip_activate) (struct thermal_zone_device *, int, |  | ||||||
| +					enum thermal_trip_activation_mode); |  | ||||||
|  	int (*get_trip_hyst) (struct thermal_zone_device *, int, int *); |  | ||||||
|  	int (*set_trip_hyst) (struct thermal_zone_device *, int, int); |  | ||||||
|  	int (*get_crit_temp) (struct thermal_zone_device *, int *); |  | ||||||
| @@ -349,6 +359,8 @@ struct thermal_genl_event { |  | ||||||
|   *		   temperature. |  | ||||||
|   * @set_trip_temp: a pointer to a function that sets the trip temperature on |  | ||||||
|   *		   hardware. |  | ||||||
| + * @activate_trip_type: a pointer to a function to enable/disable trip |  | ||||||
| + *		temperature interrupts |  | ||||||
|   */ |  | ||||||
|  struct thermal_zone_of_device_ops { |  | ||||||
|  	int (*get_temp)(void *, int *); |  | ||||||
| @@ -356,6 +368,8 @@ struct thermal_zone_of_device_ops { |  | ||||||
|  	int (*set_trips)(void *, int, int); |  | ||||||
|  	int (*set_emul_temp)(void *, int); |  | ||||||
|  	int (*set_trip_temp)(void *, int, int); |  | ||||||
| +	int (*set_trip_activate)(void *, int, |  | ||||||
| +				enum thermal_trip_activation_mode); |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  /** |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| --- a/drivers/thermal/qcom/tsens-ipq8064.c |  | ||||||
| +++ b/drivers/thermal/qcom/tsens-ipq8064.c |  | ||||||
| @@ -18,6 +18,7 @@ |  | ||||||
|  #include <linux/regmap.h> |  | ||||||
|  #include <linux/thermal.h> |  | ||||||
|  #include <linux/nvmem-consumer.h> |  | ||||||
| +#include <linux/of_platform.h> |  | ||||||
|  #include <linux/io.h> |  | ||||||
|  #include <linux/interrupt.h> |  | ||||||
|  #include "tsens.h" |  | ||||||
| @@ -320,15 +321,42 @@ static void hw_init(struct tsens_priv *p |  | ||||||
|  	INIT_WORK(&priv->tsens_work, tsens_scheduler_fn); |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +static const struct regmap_config tsens_config = { |  | ||||||
| +	.name		= "tm", |  | ||||||
| +	.reg_bits	= 32, |  | ||||||
| +	.val_bits	= 32, |  | ||||||
| +	.reg_stride	= 4, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
|  static int init_ipq8064(struct tsens_priv *priv) |  | ||||||
|  { |  | ||||||
| -	int ret, i; |  | ||||||
| +	struct device *dev = priv->dev; |  | ||||||
|  	u32 reg_cntl, offset = 0; |  | ||||||
| +	struct resource *res; |  | ||||||
| +	resource_size_t size; |  | ||||||
| +	void __iomem *base; |  | ||||||
| +	int ret, i; |  | ||||||
| +	struct platform_device *op = of_find_device_by_node(priv->dev->of_node); |  | ||||||
| + |  | ||||||
| +	if (!op) |  | ||||||
| +		return -EINVAL; |  | ||||||
|   |  | ||||||
| -	init_common(priv); |  | ||||||
| -	if (!priv->tm_map) |  | ||||||
| -		return -ENODEV; |  | ||||||
| +	/* old DTs where SROT and TM were in a contiguous 2K block */ |  | ||||||
| +	priv->tm_offset = 0x1000; |  | ||||||
|   |  | ||||||
| +	res = platform_get_resource(op, IORESOURCE_MEM, 0); |  | ||||||
| +	size = resource_size(res); |  | ||||||
| +	base = devm_ioremap(&op->dev, res->start, size); |  | ||||||
| +	if (IS_ERR(base)) { |  | ||||||
| +		ret = PTR_ERR(base); |  | ||||||
| +		goto err_put_device; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	priv->tm_map = devm_regmap_init_mmio(dev, base, &tsens_config); |  | ||||||
| +	if (IS_ERR(priv->tm_map)) { |  | ||||||
| +		ret = PTR_ERR(priv->tm_map); |  | ||||||
| +		goto err_put_device; |  | ||||||
| +	} |  | ||||||
|  	/* |  | ||||||
|  	 * The status registers for each sensor are discontiguous |  | ||||||
|  	 * because some SoCs have 5 sensors while others have more |  | ||||||
| @@ -367,6 +395,10 @@ static int init_ipq8064(struct tsens_pri |  | ||||||
|  		return ret; |  | ||||||
|   |  | ||||||
|  	return 0; |  | ||||||
| + |  | ||||||
| +err_put_device: |  | ||||||
| +	put_device(&op->dev); |  | ||||||
| +	return ret; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
|  static int calibrate_ipq8064(struct tsens_priv *priv) |  | ||||||
| @@ -1,107 +0,0 @@ | |||||||
| --- a/drivers/thermal/qcom/tsens-ipq8064.c |  | ||||||
| +++ b/drivers/thermal/qcom/tsens-ipq8064.c |  | ||||||
| @@ -13,10 +13,12 @@ |  | ||||||
|   */ |  | ||||||
|   |  | ||||||
|  #include <linux/platform_device.h> |  | ||||||
| +#include <linux/err.h> |  | ||||||
|  #include <linux/delay.h> |  | ||||||
|  #include <linux/bitops.h> |  | ||||||
|  #include <linux/regmap.h> |  | ||||||
|  #include <linux/thermal.h> |  | ||||||
| +#include <linux/slab.h> |  | ||||||
|  #include <linux/nvmem-consumer.h> |  | ||||||
|  #include <linux/of_platform.h> |  | ||||||
|  #include <linux/io.h> |  | ||||||
| @@ -211,9 +213,8 @@ static void tsens_scheduler_fn(struct wo |  | ||||||
|  	struct tsens_priv *priv = container_of(work, struct tsens_priv, |  | ||||||
|  					tsens_work); |  | ||||||
|  	unsigned int threshold, threshold_low, code, reg, sensor, mask; |  | ||||||
| -	unsigned int sensor_addr; |  | ||||||
|  	bool upper_th_x, lower_th_x; |  | ||||||
| -	int adc_code, ret; |  | ||||||
| +	int ret; |  | ||||||
|   |  | ||||||
|  	ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®); |  | ||||||
|  	if (ret) |  | ||||||
| @@ -262,9 +263,8 @@ static void tsens_scheduler_fn(struct wo |  | ||||||
|  		if (upper_th_x || lower_th_x) { |  | ||||||
|  			/* Notify user space */ |  | ||||||
|  			schedule_work(&priv->sensor[0].notify_work); |  | ||||||
| -			regmap_read(priv->tm_map, sensor_addr, &adc_code); |  | ||||||
|  			pr_debug("Trigger (%d degrees) for sensor %d\n", |  | ||||||
| -				code_to_degC(adc_code, &priv->sensor[0]), 0); |  | ||||||
| +				code_to_degC(code, &priv->sensor[0]), 0); |  | ||||||
|  		} |  | ||||||
|  	} |  | ||||||
|  	regmap_write(priv->tm_map, STATUS_CNTL_8064, reg & mask); |  | ||||||
| @@ -404,40 +404,55 @@ err_put_device: |  | ||||||
|  static int calibrate_ipq8064(struct tsens_priv *priv) |  | ||||||
|  { |  | ||||||
|  	int i; |  | ||||||
| -	char *data, *data_backup; |  | ||||||
| - |  | ||||||
| +	int ret = 0; |  | ||||||
| +	u8 *data, *data_backup; |  | ||||||
| +	struct device *dev = priv->dev; |  | ||||||
|  	ssize_t num_read = priv->num_sensors; |  | ||||||
|  	struct tsens_sensor *s = priv->sensor; |  | ||||||
|   |  | ||||||
| -	data = qfprom_read(priv->dev, "calib"); |  | ||||||
| +	data = qfprom_read(dev, "calib"); |  | ||||||
|  	if (IS_ERR(data)) { |  | ||||||
| -		pr_err("Calibration not found.\n"); |  | ||||||
| -		return PTR_ERR(data); |  | ||||||
| +		ret = PTR_ERR(data); |  | ||||||
| +		if (ret != -EPROBE_DEFER) |  | ||||||
| +			dev_err(dev, "Calibration not found."); |  | ||||||
| +		goto exit; |  | ||||||
|  	} |  | ||||||
|   |  | ||||||
| -	data_backup = qfprom_read(priv->dev, "calib_backup"); |  | ||||||
| +	data_backup = qfprom_read(dev, "calib_backup"); |  | ||||||
|  	if (IS_ERR(data_backup)) { |  | ||||||
| -		pr_err("Backup calibration not found.\n"); |  | ||||||
| -		return PTR_ERR(data_backup); |  | ||||||
| +		ret = PTR_ERR(data_backup); |  | ||||||
| +		if (ret != -EPROBE_DEFER) |  | ||||||
| +			dev_err(dev, "Backup Calibration not found."); |  | ||||||
| +		goto free_data; |  | ||||||
|  	} |  | ||||||
|   |  | ||||||
|  	for (i = 0; i < num_read; i++) { |  | ||||||
|  		s[i].calib_data = readb_relaxed(data + i); |  | ||||||
| -		s[i].calib_data_backup = readb_relaxed(data_backup + i); |  | ||||||
| +		 |  | ||||||
| +		if (!s[i].calib_data) { |  | ||||||
| +			s[i].calib_data_backup = readb_relaxed(data_backup + i); |  | ||||||
| + |  | ||||||
| +			if (!s[i].calib_data_backup) { |  | ||||||
| +				dev_err(dev, "QFPROM TSENS calibration data not present"); |  | ||||||
| +				ret = -ENODEV; |  | ||||||
| +				goto free_backup; |  | ||||||
| +			} |  | ||||||
|   |  | ||||||
| -		if (s[i].calib_data_backup) |  | ||||||
|  			s[i].calib_data = s[i].calib_data_backup; |  | ||||||
| -		if (!s[i].calib_data) { |  | ||||||
| -			pr_err("QFPROM TSENS calibration data not present\n"); |  | ||||||
| -			return -ENODEV; |  | ||||||
|  		} |  | ||||||
| + |  | ||||||
|  		s[i].slope = tsens_8064_slope[i]; |  | ||||||
|  		s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); |  | ||||||
|  	} |  | ||||||
|   |  | ||||||
|  	hw_init(priv); |  | ||||||
|   |  | ||||||
| -	return 0; |  | ||||||
| +free_backup: |  | ||||||
| +	kfree(data_backup); |  | ||||||
| +free_data: |  | ||||||
| +	kfree(data); |  | ||||||
| +exit: |  | ||||||
| +	return ret; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
|  static int get_temp_ipq8064(struct tsens_priv *priv, int id, int *temp) |  | ||||||
| @@ -0,0 +1,292 @@ | |||||||
|  | From 5c7d1181056feef0b58fb2f556f55e170ba5b479 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 25 Jul 2020 19:14:59 +0200 | ||||||
|  | Subject: [PATCH 01/10] drivers: thermal: tsens: Add VER_0 tsens version | ||||||
|  |  | ||||||
|  | VER_0 is used to describe device based on tsens version before v0.1. | ||||||
|  | These device are devices based on msm8960 for example apq8064 or | ||||||
|  | ipq806x. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | Reported-by: kernel test robot <lkp@intel.com> | ||||||
|  | Reported-by: Dan Carpenter <dan.carpenter@oracle.com> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens.c | 150 ++++++++++++++++++++++++++++------- | ||||||
|  |  drivers/thermal/qcom/tsens.h |   4 +- | ||||||
|  |  2 files changed, 124 insertions(+), 30 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c | ||||||
|  | index d8ce3a687b80..9a7e991d4bd2 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens.c | ||||||
|  | @@ -12,6 +12,7 @@ | ||||||
|  |  #include <linux/of.h> | ||||||
|  |  #include <linux/of_address.h> | ||||||
|  |  #include <linux/of_platform.h> | ||||||
|  | +#include <linux/mfd/syscon.h> | ||||||
|  |  #include <linux/platform_device.h> | ||||||
|  |  #include <linux/pm.h> | ||||||
|  |  #include <linux/regmap.h> | ||||||
|  | @@ -515,6 +516,15 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) | ||||||
|  |  			dev_dbg(priv->dev, "[%u] %s: no violation:  %d\n", | ||||||
|  |  				hw_id, __func__, temp); | ||||||
|  |  		} | ||||||
|  | + | ||||||
|  | +		if (tsens_version(priv) < VER_0_1) { | ||||||
|  | +			/* Constraint: There is only 1 interrupt control register for all | ||||||
|  | +			 * 11 temperature sensor. So monitoring more than 1 sensor based | ||||||
|  | +			 * on interrupts will yield inconsistent result. To overcome this | ||||||
|  | +			 * issue we will monitor only sensor 0 which is the master sensor. | ||||||
|  | +			 */ | ||||||
|  | +			break; | ||||||
|  | +		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	return IRQ_HANDLED; | ||||||
|  | @@ -530,6 +540,13 @@ static int tsens_set_trips(void *_sensor, int low, int high) | ||||||
|  |  	int high_val, low_val, cl_high, cl_low; | ||||||
|  |  	u32 hw_id = s->hw_id; | ||||||
|  |   | ||||||
|  | +	if (tsens_version(priv) < VER_0_1) { | ||||||
|  | +		/* Pre v0.1 IP had a single register for each type of interrupt | ||||||
|  | +		 * and thresholds | ||||||
|  | +		 */ | ||||||
|  | +		hw_id = 0; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", | ||||||
|  |  		hw_id, __func__, low, high); | ||||||
|  |   | ||||||
|  | @@ -584,18 +601,21 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) | ||||||
|  |  	u32 valid; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	ret = regmap_field_read(priv->rf[valid_idx], &valid); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | -	while (!valid) { | ||||||
|  | -		/* Valid bit is 0 for 6 AHB clock cycles. | ||||||
|  | -		 * At 19.2MHz, 1 AHB clock is ~60ns. | ||||||
|  | -		 * We should enter this loop very, very rarely. | ||||||
|  | -		 */ | ||||||
|  | -		ndelay(400); | ||||||
|  | +	/* VER_0 doesn't have VALID bit */ | ||||||
|  | +	if (tsens_version(priv) >= VER_0_1) { | ||||||
|  |  		ret = regmap_field_read(priv->rf[valid_idx], &valid); | ||||||
|  |  		if (ret) | ||||||
|  |  			return ret; | ||||||
|  | +		while (!valid) { | ||||||
|  | +			/* Valid bit is 0 for 6 AHB clock cycles. | ||||||
|  | +			 * At 19.2MHz, 1 AHB clock is ~60ns. | ||||||
|  | +			 * We should enter this loop very, very rarely. | ||||||
|  | +			 */ | ||||||
|  | +			ndelay(400); | ||||||
|  | +			ret = regmap_field_read(priv->rf[valid_idx], &valid); | ||||||
|  | +			if (ret) | ||||||
|  | +				return ret; | ||||||
|  | +		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	/* Valid bit is set, OK to read the temperature */ | ||||||
|  | @@ -608,15 +628,29 @@ int get_temp_common(const struct tsens_sensor *s, int *temp) | ||||||
|  |  { | ||||||
|  |  	struct tsens_priv *priv = s->priv; | ||||||
|  |  	int hw_id = s->hw_id; | ||||||
|  | -	int last_temp = 0, ret; | ||||||
|  | +	int last_temp = 0, ret, trdy; | ||||||
|  | +	unsigned long timeout; | ||||||
|  |   | ||||||
|  | -	ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | +	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | ||||||
|  | +	do { | ||||||
|  | +		if (tsens_version(priv) == VER_0) { | ||||||
|  | +			ret = regmap_field_read(priv->rf[TRDY], &trdy); | ||||||
|  | +			if (ret) | ||||||
|  | +				return ret; | ||||||
|  | +			if (!trdy) | ||||||
|  | +				continue; | ||||||
|  | +		} | ||||||
|  |   | ||||||
|  | -	*temp = code_to_degc(last_temp, s) * 1000; | ||||||
|  | +		ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); | ||||||
|  | +		if (ret) | ||||||
|  | +			return ret; | ||||||
|  |   | ||||||
|  | -	return 0; | ||||||
|  | +		*temp = code_to_degc(last_temp, s) * 1000; | ||||||
|  | + | ||||||
|  | +		return 0; | ||||||
|  | +	} while (time_before(jiffies, timeout)); | ||||||
|  | + | ||||||
|  | +	return -ETIMEDOUT; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  #ifdef CONFIG_DEBUG_FS | ||||||
|  | @@ -738,19 +772,34 @@ int __init init_common(struct tsens_priv *priv) | ||||||
|  |  		priv->tm_offset = 0x1000; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	res = platform_get_resource(op, IORESOURCE_MEM, 0); | ||||||
|  | -	tm_base = devm_ioremap_resource(dev, res); | ||||||
|  | -	if (IS_ERR(tm_base)) { | ||||||
|  | -		ret = PTR_ERR(tm_base); | ||||||
|  | -		goto err_put_device; | ||||||
|  | +	if (tsens_version(priv) >= VER_0_1) { | ||||||
|  | +		res = platform_get_resource(op, IORESOURCE_MEM, 0); | ||||||
|  | +		tm_base = devm_ioremap_resource(dev, res); | ||||||
|  | +		if (IS_ERR(tm_base)) { | ||||||
|  | +			ret = PTR_ERR(tm_base); | ||||||
|  | +			goto err_put_device; | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  | +		priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); | ||||||
|  | +	} else { /* VER_0 share the same gcc regs using a syscon */ | ||||||
|  | +		struct device *parent = priv->dev->parent; | ||||||
|  | + | ||||||
|  | +		if (parent) | ||||||
|  | +			priv->tm_map = syscon_node_to_regmap(parent->of_node); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); | ||||||
|  | -	if (IS_ERR(priv->tm_map)) { | ||||||
|  | -		ret = PTR_ERR(priv->tm_map); | ||||||
|  | +	if (IS_ERR_OR_NULL(priv->tm_map)) { | ||||||
|  | +		if (!priv->tm_map) | ||||||
|  | +			ret = -ENODEV; | ||||||
|  | +		else | ||||||
|  | +			ret = PTR_ERR(priv->tm_map); | ||||||
|  |  		goto err_put_device; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	/* VER_0 have only tm_map */ | ||||||
|  | +	if (!priv->srot_map) | ||||||
|  | +		priv->srot_map = priv->tm_map; | ||||||
|  | + | ||||||
|  |  	if (tsens_version(priv) > VER_0_1) { | ||||||
|  |  		for (i = VER_MAJOR; i <= VER_STEP; i++) { | ||||||
|  |  			priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, | ||||||
|  | @@ -769,6 +818,10 @@ int __init init_common(struct tsens_priv *priv) | ||||||
|  |  		ret = PTR_ERR(priv->rf[TSENS_EN]); | ||||||
|  |  		goto err_put_device; | ||||||
|  |  	} | ||||||
|  | +	/* in VER_0 TSENS need to be explicitly enabled */ | ||||||
|  | +	if (tsens_version(priv) == VER_0) | ||||||
|  | +		regmap_field_write(priv->rf[TSENS_EN], 1); | ||||||
|  | + | ||||||
|  |  	ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); | ||||||
|  |  	if (ret) | ||||||
|  |  		goto err_put_device; | ||||||
|  | @@ -791,6 +844,19 @@ int __init init_common(struct tsens_priv *priv) | ||||||
|  |  		goto err_put_device; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	priv->rf[TSENS_SW_RST] = | ||||||
|  | +		devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]); | ||||||
|  | +	if (IS_ERR(priv->rf[TSENS_SW_RST])) { | ||||||
|  | +		ret = PTR_ERR(priv->rf[TSENS_SW_RST]); | ||||||
|  | +		goto err_put_device; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]); | ||||||
|  | +	if (IS_ERR(priv->rf[TRDY])) { | ||||||
|  | +		ret = PTR_ERR(priv->rf[TRDY]); | ||||||
|  | +		goto err_put_device; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	/* This loop might need changes if enum regfield_ids is reordered */ | ||||||
|  |  	for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { | ||||||
|  |  		for (i = 0; i < priv->feat->max_sensors; i++) { | ||||||
|  | @@ -806,7 +872,7 @@ int __init init_common(struct tsens_priv *priv) | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (priv->feat->crit_int) { | ||||||
|  | +	if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) { | ||||||
|  |  		/* Loop might need changes if enum regfield_ids is reordered */ | ||||||
|  |  		for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { | ||||||
|  |  			for (i = 0; i < priv->feat->max_sensors; i++) { | ||||||
|  | @@ -844,7 +910,11 @@ int __init init_common(struct tsens_priv *priv) | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	spin_lock_init(&priv->ul_lock); | ||||||
|  | -	tsens_enable_irq(priv); | ||||||
|  | + | ||||||
|  | +	/* VER_0 interrupt doesn't need to be enabled */ | ||||||
|  | +	if (tsens_version(priv) >= VER_0_1) | ||||||
|  | +		tsens_enable_irq(priv); | ||||||
|  | + | ||||||
|  |  	tsens_debug_init(op); | ||||||
|  |   | ||||||
|  |  err_put_device: | ||||||
|  | @@ -943,10 +1013,19 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, | ||||||
|  |  		if (irq == -ENXIO) | ||||||
|  |  			ret = 0; | ||||||
|  |  	} else { | ||||||
|  | -		ret = devm_request_threaded_irq(&pdev->dev, irq, | ||||||
|  | -						NULL, thread_fn, | ||||||
|  | -						IRQF_ONESHOT, | ||||||
|  | -						dev_name(&pdev->dev), priv); | ||||||
|  | +		/* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */ | ||||||
|  | +		if (tsens_version(priv) == VER_0) | ||||||
|  | +			ret = devm_request_threaded_irq(&pdev->dev, irq, | ||||||
|  | +							thread_fn, NULL, | ||||||
|  | +							IRQF_TRIGGER_RISING, | ||||||
|  | +							dev_name(&pdev->dev), | ||||||
|  | +							priv); | ||||||
|  | +		else | ||||||
|  | +			ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | ||||||
|  | +							thread_fn, IRQF_ONESHOT, | ||||||
|  | +							dev_name(&pdev->dev), | ||||||
|  | +							priv); | ||||||
|  | + | ||||||
|  |  		if (ret) | ||||||
|  |  			dev_err(&pdev->dev, "%s: failed to get irq\n", | ||||||
|  |  				__func__); | ||||||
|  | @@ -975,6 +1054,19 @@ static int tsens_register(struct tsens_priv *priv) | ||||||
|  |  			priv->ops->enable(priv, i); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	/* VER_0 require to set MIN and MAX THRESH | ||||||
|  | +	 * These 2 regs are set using the: | ||||||
|  | +	 * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C | ||||||
|  | +	 * - CRIT_THRESH_1 for MIN THRESH hardcoded to   0°C | ||||||
|  | +	 */ | ||||||
|  | +	if (tsens_version(priv) < VER_0_1) { | ||||||
|  | +		regmap_field_write(priv->rf[CRIT_THRESH_0], | ||||||
|  | +				   tsens_mC_to_hw(priv->sensor, 120000)); | ||||||
|  | + | ||||||
|  | +		regmap_field_write(priv->rf[CRIT_THRESH_1], | ||||||
|  | +				   tsens_mC_to_hw(priv->sensor, 0)); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h | ||||||
|  | index f40b625f897e..8e6c1fd3ccf5 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens.h | ||||||
|  | +++ b/drivers/thermal/qcom/tsens.h | ||||||
|  | @@ -13,6 +13,7 @@ | ||||||
|  |  #define CAL_DEGC_PT2		120 | ||||||
|  |  #define SLOPE_FACTOR		1000 | ||||||
|  |  #define SLOPE_DEFAULT		3200 | ||||||
|  | +#define TIMEOUT_US		100 | ||||||
|  |  #define THRESHOLD_MAX_ADC_CODE	0x3ff | ||||||
|  |  #define THRESHOLD_MIN_ADC_CODE	0x0 | ||||||
|  |   | ||||||
|  | @@ -25,7 +26,8 @@ struct tsens_priv; | ||||||
|  |   | ||||||
|  |  /* IP version numbers in ascending order */ | ||||||
|  |  enum tsens_ver { | ||||||
|  | -	VER_0_1 = 0, | ||||||
|  | +	VER_0 = 0, | ||||||
|  | +	VER_0_1, | ||||||
|  |  	VER_1_X, | ||||||
|  |  	VER_2_X, | ||||||
|  |  }; | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | From efa0d50a6c5ec7619371dfe4d3e6ca54b73787d5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 25 Nov 2020 16:47:21 +0100 | ||||||
|  | Subject: [PATCH 02/10] drivers: thermal: tsens: Don't hardcode sensor slope | ||||||
|  |  | ||||||
|  | Function compute_intercept_slope hardcode the sensor slope to | ||||||
|  | SLOPE_DEFAULT. Change this and use the default value only if a slope is | ||||||
|  | not defined. This is needed for tsens VER_0 that has a hardcoded slope | ||||||
|  | table. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens.c | 3 ++- | ||||||
|  |  1 file changed, 2 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c | ||||||
|  | index 9a7e991d4bd2..38b9936def1a 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens.c | ||||||
|  | @@ -86,7 +86,8 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, | ||||||
|  |  			"%s: sensor%d - data_point1:%#x data_point2:%#x\n", | ||||||
|  |  			__func__, i, p1[i], p2[i]); | ||||||
|  |   | ||||||
|  | -		priv->sensor[i].slope = SLOPE_DEFAULT; | ||||||
|  | +		if (!priv->sensor[i].slope) | ||||||
|  | +			priv->sensor[i].slope = SLOPE_DEFAULT; | ||||||
|  |  		if (mode == TWO_PT_CALIB) { | ||||||
|  |  			/* | ||||||
|  |  			 * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,124 @@ | |||||||
|  | From 6bac2e2fa36c2d7c304768a689d8b73155b90aa2 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 25 Nov 2020 17:15:51 +0100 | ||||||
|  | Subject: [PATCH 03/10] drivers: thermal: tsens: Convert msm8960 to reg_field | ||||||
|  |  | ||||||
|  | Convert msm9860 driver to reg_field to use the init_common | ||||||
|  | function. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Acked-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 80 ++++++++++++++++++++++++++++++- | ||||||
|  |  1 file changed, 79 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 2a28a5af209e..3f4fc1ffe679 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -51,11 +51,22 @@ | ||||||
|  |  #define MIN_LIMIT_TH		0x0 | ||||||
|  |  #define MAX_LIMIT_TH		0xff | ||||||
|  |   | ||||||
|  | -#define S0_STATUS_ADDR		0x3628 | ||||||
|  |  #define INT_STATUS_ADDR		0x363c | ||||||
|  |  #define TRDY_MASK		BIT(7) | ||||||
|  |  #define TIMEOUT_US		100 | ||||||
|  |   | ||||||
|  | +#define S0_STATUS_OFF		0x3628 | ||||||
|  | +#define S1_STATUS_OFF		0x362c | ||||||
|  | +#define S2_STATUS_OFF		0x3630 | ||||||
|  | +#define S3_STATUS_OFF		0x3634 | ||||||
|  | +#define S4_STATUS_OFF		0x3638 | ||||||
|  | +#define S5_STATUS_OFF		0x3664  /* Sensors 5-10 found on apq8064/msm8960 */ | ||||||
|  | +#define S6_STATUS_OFF		0x3668 | ||||||
|  | +#define S7_STATUS_OFF		0x366c | ||||||
|  | +#define S8_STATUS_OFF		0x3670 | ||||||
|  | +#define S9_STATUS_OFF		0x3674 | ||||||
|  | +#define S10_STATUS_OFF		0x3678 | ||||||
|  | + | ||||||
|  |  static int suspend_8960(struct tsens_priv *priv) | ||||||
|  |  { | ||||||
|  |  	int ret; | ||||||
|  | @@ -269,6 +280,71 @@ static int get_temp_8960(const struct tsens_sensor *s, int *temp) | ||||||
|  |  	return -ETIMEDOUT; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static struct tsens_features tsens_8960_feat = { | ||||||
|  | +	.ver_major	= VER_0, | ||||||
|  | +	.crit_int	= 0, | ||||||
|  | +	.adc		= 1, | ||||||
|  | +	.srot_split	= 0, | ||||||
|  | +	.max_sensors	= 11, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { | ||||||
|  | +	/* ----- SROT ------ */ | ||||||
|  | +	/* No VERSION information */ | ||||||
|  | + | ||||||
|  | +	/* CNTL */ | ||||||
|  | +	[TSENS_EN]     = REG_FIELD(CNTL_ADDR,  0, 0), | ||||||
|  | +	[TSENS_SW_RST] = REG_FIELD(CNTL_ADDR,  1, 1), | ||||||
|  | +	/* 8960 has 5 sensors, 8660 has 11, we only handle 5 */ | ||||||
|  | +	[SENSOR_EN]    = REG_FIELD(CNTL_ADDR,  3, 7), | ||||||
|  | + | ||||||
|  | +	/* ----- TM ------ */ | ||||||
|  | +	/* INTERRUPT ENABLE */ | ||||||
|  | +	/* NO INTERRUPT ENABLE */ | ||||||
|  | + | ||||||
|  | +	/* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */ | ||||||
|  | +	[LOW_THRESH_0]   = REG_FIELD(THRESHOLD_ADDR,  0,  7), | ||||||
|  | +	[UP_THRESH_0]    = REG_FIELD(THRESHOLD_ADDR,  8, 15), | ||||||
|  | +	/* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield | ||||||
|  | +	 * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp | ||||||
|  | +	 * MIN_THRESH_0 -> CRIT_THRESH_1 | ||||||
|  | +	 * MAX_THRESH_0 -> CRIT_THRESH_0 | ||||||
|  | +	 */ | ||||||
|  | +	[CRIT_THRESH_1]   = REG_FIELD(THRESHOLD_ADDR, 16, 23), | ||||||
|  | +	[CRIT_THRESH_0]   = REG_FIELD(THRESHOLD_ADDR, 24, 31), | ||||||
|  | + | ||||||
|  | +	/* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */ | ||||||
|  | +	/* 1 == clear, 0 == normal operation */ | ||||||
|  | +	[LOW_INT_CLEAR_0]   = REG_FIELD(CNTL_ADDR,  9,  9), | ||||||
|  | +	[UP_INT_CLEAR_0]    = REG_FIELD(CNTL_ADDR, 10, 10), | ||||||
|  | + | ||||||
|  | +	/* NO CRITICAL INTERRUPT SUPPORT on 8960 */ | ||||||
|  | + | ||||||
|  | +	/* Sn_STATUS */ | ||||||
|  | +	[LAST_TEMP_0]  = REG_FIELD(S0_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_1]  = REG_FIELD(S1_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_2]  = REG_FIELD(S2_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_3]  = REG_FIELD(S3_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_4]  = REG_FIELD(S4_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_5]  = REG_FIELD(S5_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_6]  = REG_FIELD(S6_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_7]  = REG_FIELD(S7_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_8]  = REG_FIELD(S8_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_9]  = REG_FIELD(S9_STATUS_OFF,  0,  7), | ||||||
|  | +	[LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0,  7), | ||||||
|  | + | ||||||
|  | +	/* No VALID field on 8960 */ | ||||||
|  | +	/* TSENS_INT_STATUS bits: 1 == threshold violated */ | ||||||
|  | +	[MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0), | ||||||
|  | +	[LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1), | ||||||
|  | +	[UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2), | ||||||
|  | +	/* No CRITICAL field on 8960 */ | ||||||
|  | +	[MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3), | ||||||
|  | + | ||||||
|  | +	/* TRDY: 1=ready, 0=in progress */ | ||||||
|  | +	[TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static const struct tsens_ops ops_8960 = { | ||||||
|  |  	.init		= init_8960, | ||||||
|  |  	.calibrate	= calibrate_8960, | ||||||
|  | @@ -282,4 +358,6 @@ static const struct tsens_ops ops_8960 = { | ||||||
|  |  struct tsens_plat_data data_8960 = { | ||||||
|  |  	.num_sensors	= 11, | ||||||
|  |  	.ops		= &ops_8960, | ||||||
|  | +	.feat		= &tsens_8960_feat, | ||||||
|  | +	.fields		= tsens_8960_regfields, | ||||||
|  |  }; | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,86 @@ | |||||||
|  | From c04f98a496929f75d75c65115d5717423c3d0634 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 25 Nov 2020 17:16:36 +0100 | ||||||
|  | Subject: [PATCH 04/10] drivers: thermal: tsens: Use init_common for msm8960 | ||||||
|  |  | ||||||
|  | Use init_common and drop custom init for msm8960. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 52 +------------------------------ | ||||||
|  |  1 file changed, 1 insertion(+), 51 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 3f4fc1ffe679..86585f439985 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -173,56 +173,6 @@ static void disable_8960(struct tsens_priv *priv) | ||||||
|  |  	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int init_8960(struct tsens_priv *priv) | ||||||
|  | -{ | ||||||
|  | -	int ret, i; | ||||||
|  | -	u32 reg_cntl; | ||||||
|  | - | ||||||
|  | -	priv->tm_map = dev_get_regmap(priv->dev, NULL); | ||||||
|  | -	if (!priv->tm_map) | ||||||
|  | -		return -ENODEV; | ||||||
|  | - | ||||||
|  | -	/* | ||||||
|  | -	 * The status registers for each sensor are discontiguous | ||||||
|  | -	 * because some SoCs have 5 sensors while others have more | ||||||
|  | -	 * but the control registers stay in the same place, i.e | ||||||
|  | -	 * directly after the first 5 status registers. | ||||||
|  | -	 */ | ||||||
|  | -	for (i = 0; i < priv->num_sensors; i++) { | ||||||
|  | -		if (i >= 5) | ||||||
|  | -			priv->sensor[i].status = S0_STATUS_ADDR + 40; | ||||||
|  | -		priv->sensor[i].status += i * 4; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	reg_cntl = SW_RST; | ||||||
|  | -	ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	if (priv->num_sensors > 1) { | ||||||
|  | -		reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); | ||||||
|  | -		reg_cntl &= ~SW_RST; | ||||||
|  | -		ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, | ||||||
|  | -					 CONFIG_MASK, CONFIG); | ||||||
|  | -	} else { | ||||||
|  | -		reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); | ||||||
|  | -		reg_cntl &= ~CONFIG_MASK_8660; | ||||||
|  | -		reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; | ||||||
|  | -	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	reg_cntl |= EN; | ||||||
|  | -	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static int calibrate_8960(struct tsens_priv *priv) | ||||||
|  |  { | ||||||
|  |  	int i; | ||||||
|  | @@ -346,7 +296,7 @@ static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct tsens_ops ops_8960 = { | ||||||
|  | -	.init		= init_8960, | ||||||
|  | +	.init		= init_common, | ||||||
|  |  	.calibrate	= calibrate_8960, | ||||||
|  |  	.get_temp	= get_temp_8960, | ||||||
|  |  	.enable		= enable_8960, | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,71 @@ | |||||||
|  | From b3e8bd33b84a6b6c863bd1733bd15b5f1483b8ab Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 25 Nov 2020 17:06:55 +0100 | ||||||
|  | Subject: [PATCH 05/10] drivers: thermal: tsens: Fix bug in sensor enable for | ||||||
|  |  msm8960 | ||||||
|  |  | ||||||
|  | Device based on tsens VER_0 contains a hardware bug that results in some | ||||||
|  | problem with sensor enablement. Sensor id 6-11 can't be enabled | ||||||
|  | selectively and all of them must be enabled in one step. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Acked-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 23 ++++++++++++++++++++--- | ||||||
|  |  1 file changed, 20 insertions(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 86585f439985..95fcccafae14 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -27,9 +27,9 @@ | ||||||
|  |  #define EN			BIT(0) | ||||||
|  |  #define SW_RST			BIT(1) | ||||||
|  |  #define SENSOR0_EN		BIT(3) | ||||||
|  | +#define MEASURE_PERIOD		BIT(18) | ||||||
|  |  #define SLP_CLK_ENA		BIT(26) | ||||||
|  |  #define SLP_CLK_ENA_8660	BIT(24) | ||||||
|  | -#define MEASURE_PERIOD		1 | ||||||
|  |  #define SENSOR0_SHIFT		3 | ||||||
|  |   | ||||||
|  |  /* INT_STATUS_ADDR bitmasks */ | ||||||
|  | @@ -126,17 +126,34 @@ static int resume_8960(struct tsens_priv *priv) | ||||||
|  |  static int enable_8960(struct tsens_priv *priv, int id) | ||||||
|  |  { | ||||||
|  |  	int ret; | ||||||
|  | -	u32 reg, mask; | ||||||
|  | +	u32 reg, mask = BIT(id); | ||||||
|  |   | ||||||
|  |  	ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); | ||||||
|  |  	if (ret) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	mask = BIT(id + SENSOR0_SHIFT); | ||||||
|  | +	/* HARDWARE BUG: | ||||||
|  | +	 * On platforms with more than 6 sensors, all remaining sensors | ||||||
|  | +	 * must be enabled together, otherwise undefined results are expected. | ||||||
|  | +	 * (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver, | ||||||
|  | +	 * all the sensors are enabled in one step hence this bug is not | ||||||
|  | +	 * triggered. | ||||||
|  | +	 */ | ||||||
|  | +	if (id > 5) | ||||||
|  | +		mask = GENMASK(10, 6); | ||||||
|  | + | ||||||
|  | +	mask <<= SENSOR0_SHIFT; | ||||||
|  | + | ||||||
|  | +	/* Sensors already enabled. Skip. */ | ||||||
|  | +	if ((reg & mask) == mask) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  |  	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); | ||||||
|  |  	if (ret) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | +	reg |= MEASURE_PERIOD; | ||||||
|  | + | ||||||
|  |  	if (priv->num_sensors > 1) | ||||||
|  |  		reg |= mask | SLP_CLK_ENA | EN; | ||||||
|  |  	else | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,114 @@ | |||||||
|  | From 1ff9f982051759e0387e8c7e793b49c48eae291d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 25 Nov 2020 17:11:05 +0100 | ||||||
|  | Subject: [PATCH 06/10] drivers: thermal: tsens: Replace custom 8960 apis with | ||||||
|  |  generic apis | ||||||
|  |  | ||||||
|  | Rework calibrate function to use common function. Derive the offset from | ||||||
|  | a missing hardcoded slope table and the data from the nvmem calib | ||||||
|  | efuses. | ||||||
|  | Drop custom get_temp function and use generic api. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Acked-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 56 +++++++++---------------------- | ||||||
|  |  1 file changed, 15 insertions(+), 41 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 95fcccafae14..9cc8a7dd23ae 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -67,6 +67,13 @@ | ||||||
|  |  #define S9_STATUS_OFF		0x3674 | ||||||
|  |  #define S10_STATUS_OFF		0x3678 | ||||||
|  |   | ||||||
|  | +/* Original slope - 200 to compensate mC to C inaccuracy */ | ||||||
|  | +static u32 tsens_msm8960_slope[] = { | ||||||
|  | +			976, 976, 954, 976, | ||||||
|  | +			911, 932, 932, 999, | ||||||
|  | +			932, 999, 932 | ||||||
|  | +			}; | ||||||
|  | + | ||||||
|  |  static int suspend_8960(struct tsens_priv *priv) | ||||||
|  |  { | ||||||
|  |  	int ret; | ||||||
|  | @@ -194,9 +201,7 @@ static int calibrate_8960(struct tsens_priv *priv) | ||||||
|  |  { | ||||||
|  |  	int i; | ||||||
|  |  	char *data; | ||||||
|  | - | ||||||
|  | -	ssize_t num_read = priv->num_sensors; | ||||||
|  | -	struct tsens_sensor *s = priv->sensor; | ||||||
|  | +	u32 p1[11]; | ||||||
|  |   | ||||||
|  |  	data = qfprom_read(priv->dev, "calib"); | ||||||
|  |  	if (IS_ERR(data)) | ||||||
|  | @@ -204,49 +209,18 @@ static int calibrate_8960(struct tsens_priv *priv) | ||||||
|  |  	if (IS_ERR(data)) | ||||||
|  |  		return PTR_ERR(data); | ||||||
|  |   | ||||||
|  | -	for (i = 0; i < num_read; i++, s++) | ||||||
|  | -		s->offset = data[i]; | ||||||
|  | +	for (i = 0; i < priv->num_sensors; i++) { | ||||||
|  | +		p1[i] = data[i]; | ||||||
|  | +		priv->sensor[i].slope = tsens_msm8960_slope[i]; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB); | ||||||
|  |   | ||||||
|  |  	kfree(data); | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -/* Temperature on y axis and ADC-code on x-axis */ | ||||||
|  | -static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) | ||||||
|  | -{ | ||||||
|  | -	int slope, offset; | ||||||
|  | - | ||||||
|  | -	slope = thermal_zone_get_slope(s->tzd); | ||||||
|  | -	offset = CAL_MDEGC - slope * s->offset; | ||||||
|  | - | ||||||
|  | -	return adc_code * slope + offset; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int get_temp_8960(const struct tsens_sensor *s, int *temp) | ||||||
|  | -{ | ||||||
|  | -	int ret; | ||||||
|  | -	u32 code, trdy; | ||||||
|  | -	struct tsens_priv *priv = s->priv; | ||||||
|  | -	unsigned long timeout; | ||||||
|  | - | ||||||
|  | -	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | ||||||
|  | -	do { | ||||||
|  | -		ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); | ||||||
|  | -		if (ret) | ||||||
|  | -			return ret; | ||||||
|  | -		if (!(trdy & TRDY_MASK)) | ||||||
|  | -			continue; | ||||||
|  | -		ret = regmap_read(priv->tm_map, s->status, &code); | ||||||
|  | -		if (ret) | ||||||
|  | -			return ret; | ||||||
|  | -		*temp = code_to_mdegC(code, s); | ||||||
|  | -		return 0; | ||||||
|  | -	} while (time_before(jiffies, timeout)); | ||||||
|  | - | ||||||
|  | -	return -ETIMEDOUT; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static struct tsens_features tsens_8960_feat = { | ||||||
|  |  	.ver_major	= VER_0, | ||||||
|  |  	.crit_int	= 0, | ||||||
|  | @@ -315,7 +289,7 @@ static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { | ||||||
|  |  static const struct tsens_ops ops_8960 = { | ||||||
|  |  	.init		= init_common, | ||||||
|  |  	.calibrate	= calibrate_8960, | ||||||
|  | -	.get_temp	= get_temp_8960, | ||||||
|  | +	.get_temp	= get_temp_common, | ||||||
|  |  	.enable		= enable_8960, | ||||||
|  |  	.disable	= disable_8960, | ||||||
|  |  	.suspend	= suspend_8960, | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,70 @@ | |||||||
|  | From 5716a61239c6ac9ceb137e825e93c3aea06c4634 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Fri, 19 Mar 2021 00:48:23 +0100 | ||||||
|  | Subject: [PATCH 07/10] drivers: thermal: tsens: Drop unused define for msm8960 | ||||||
|  |  | ||||||
|  | Drop unused define for msm8960 replaced by generic api and reg_field. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 24 +----------------------- | ||||||
|  |  1 file changed, 1 insertion(+), 23 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 9cc8a7dd23ae..58d09e927383 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -10,8 +10,6 @@ | ||||||
|  |  #include <linux/thermal.h> | ||||||
|  |  #include "tsens.h" | ||||||
|  |   | ||||||
|  | -#define CAL_MDEGC		30000 | ||||||
|  | - | ||||||
|  |  #define CONFIG_ADDR		0x3640 | ||||||
|  |  #define CONFIG_ADDR_8660	0x3620 | ||||||
|  |  /* CONFIG_ADDR bitmasks */ | ||||||
|  | @@ -21,39 +19,19 @@ | ||||||
|  |  #define CONFIG_SHIFT_8660	28 | ||||||
|  |  #define CONFIG_MASK_8660	(3 << CONFIG_SHIFT_8660) | ||||||
|  |   | ||||||
|  | -#define STATUS_CNTL_ADDR_8064	0x3660 | ||||||
|  |  #define CNTL_ADDR		0x3620 | ||||||
|  |  /* CNTL_ADDR bitmasks */ | ||||||
|  |  #define EN			BIT(0) | ||||||
|  |  #define SW_RST			BIT(1) | ||||||
|  | -#define SENSOR0_EN		BIT(3) | ||||||
|  | + | ||||||
|  |  #define MEASURE_PERIOD		BIT(18) | ||||||
|  |  #define SLP_CLK_ENA		BIT(26) | ||||||
|  |  #define SLP_CLK_ENA_8660	BIT(24) | ||||||
|  |  #define SENSOR0_SHIFT		3 | ||||||
|  |   | ||||||
|  | -/* INT_STATUS_ADDR bitmasks */ | ||||||
|  | -#define MIN_STATUS_MASK		BIT(0) | ||||||
|  | -#define LOWER_STATUS_CLR	BIT(1) | ||||||
|  | -#define UPPER_STATUS_CLR	BIT(2) | ||||||
|  | -#define MAX_STATUS_MASK		BIT(3) | ||||||
|  | - | ||||||
|  |  #define THRESHOLD_ADDR		0x3624 | ||||||
|  | -/* THRESHOLD_ADDR bitmasks */ | ||||||
|  | -#define THRESHOLD_MAX_LIMIT_SHIFT	24 | ||||||
|  | -#define THRESHOLD_MIN_LIMIT_SHIFT	16 | ||||||
|  | -#define THRESHOLD_UPPER_LIMIT_SHIFT	8 | ||||||
|  | -#define THRESHOLD_LOWER_LIMIT_SHIFT	0 | ||||||
|  | - | ||||||
|  | -/* Initial temperature threshold values */ | ||||||
|  | -#define LOWER_LIMIT_TH		0x50 | ||||||
|  | -#define UPPER_LIMIT_TH		0xdf | ||||||
|  | -#define MIN_LIMIT_TH		0x0 | ||||||
|  | -#define MAX_LIMIT_TH		0xff | ||||||
|  |   | ||||||
|  |  #define INT_STATUS_ADDR		0x363c | ||||||
|  | -#define TRDY_MASK		BIT(7) | ||||||
|  | -#define TIMEOUT_US		100 | ||||||
|  |   | ||||||
|  |  #define S0_STATUS_OFF		0x3628 | ||||||
|  |  #define S1_STATUS_OFF		0x362c | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | From 0d0c22a59bf2672b57e23da9a9ea743e91b71f54 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 25 Jul 2020 19:55:57 +0200 | ||||||
|  | Subject: [PATCH 08/10] drivers: thermal: tsens: Add support for ipq8064-tsens | ||||||
|  |  | ||||||
|  | Add support for tsens present in ipq806x SoCs based on generic msm8960 | ||||||
|  | tsens driver. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens.c | 3 +++ | ||||||
|  |  1 file changed, 3 insertions(+) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c | ||||||
|  | index 38b9936def1a..58073dc5d30b 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens.c | ||||||
|  | @@ -966,6 +966,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); | ||||||
|  |   | ||||||
|  |  static const struct of_device_id tsens_table[] = { | ||||||
|  |  	{ | ||||||
|  | +		.compatible = "qcom,ipq8064-tsens", | ||||||
|  | +		.data = &data_8960, | ||||||
|  | +	}, { | ||||||
|  |  		.compatible = "qcom,msm8916-tsens", | ||||||
|  |  		.data = &data_8916, | ||||||
|  |  	}, { | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,115 @@ | |||||||
|  | From ac369071920d427dd484cf74cddba2774bba45f5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Thu, 9 Jul 2020 22:35:54 +0200 | ||||||
|  | Subject: [PATCH 09/10] dt-bindings: thermal: tsens: Document ipq8064 bindings | ||||||
|  |  | ||||||
|  | Document the use of bindings used for msm8960 tsens based devices. | ||||||
|  | msm8960 use the same gcc regs and is set as a child of the qcom gcc. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Rob Herring <robh@kernel.org> | ||||||
|  | --- | ||||||
|  |  .../bindings/thermal/qcom-tsens.yaml          | 56 ++++++++++++++++--- | ||||||
|  |  1 file changed, 48 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | ||||||
|  | index 95462e071ab4..1785b1c75a3c 100644 | ||||||
|  | --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | ||||||
|  | +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | ||||||
|  | @@ -19,6 +19,11 @@ description: | | ||||||
|  |  properties: | ||||||
|  |    compatible: | ||||||
|  |      oneOf: | ||||||
|  | +      - description: msm9860 TSENS based | ||||||
|  | +        items: | ||||||
|  | +          - enum: | ||||||
|  | +              - qcom,ipq8064-tsens | ||||||
|  | + | ||||||
|  |        - description: v0.1 of TSENS | ||||||
|  |          items: | ||||||
|  |            - enum: | ||||||
|  | @@ -73,7 +78,9 @@ properties: | ||||||
|  |      maxItems: 2 | ||||||
|  |      items: | ||||||
|  |        - const: calib | ||||||
|  | -      - const: calib_sel | ||||||
|  | +      - enum: | ||||||
|  | +          - calib_backup | ||||||
|  | +          - calib_sel | ||||||
|  |   | ||||||
|  |    "#qcom,sensors": | ||||||
|  |      description: | ||||||
|  | @@ -88,12 +95,20 @@ properties: | ||||||
|  |        Number of cells required to uniquely identify the thermal sensors. Since | ||||||
|  |        we have multiple sensors this is set to 1 | ||||||
|  |   | ||||||
|  | +required: | ||||||
|  | +  - compatible | ||||||
|  | +  - interrupts | ||||||
|  | +  - interrupt-names | ||||||
|  | +  - "#thermal-sensor-cells" | ||||||
|  | +  - "#qcom,sensors" | ||||||
|  | + | ||||||
|  |  allOf: | ||||||
|  |    - if: | ||||||
|  |        properties: | ||||||
|  |          compatible: | ||||||
|  |            contains: | ||||||
|  |              enum: | ||||||
|  | +              - qcom,ipq8064-tsens | ||||||
|  |                - qcom,msm8916-tsens | ||||||
|  |                - qcom,msm8974-tsens | ||||||
|  |                - qcom,msm8976-tsens | ||||||
|  | @@ -114,17 +129,42 @@ allOf: | ||||||
|  |          interrupt-names: | ||||||
|  |            minItems: 2 | ||||||
|  |   | ||||||
|  | -required: | ||||||
|  | -  - compatible | ||||||
|  | -  - reg | ||||||
|  | -  - "#qcom,sensors" | ||||||
|  | -  - interrupts | ||||||
|  | -  - interrupt-names | ||||||
|  | -  - "#thermal-sensor-cells" | ||||||
|  | +  - if: | ||||||
|  | +      properties: | ||||||
|  | +        compatible: | ||||||
|  | +          contains: | ||||||
|  | +            enum: | ||||||
|  | +              - qcom,tsens-v0_1 | ||||||
|  | +              - qcom,tsens-v1 | ||||||
|  | +              - qcom,tsens-v2 | ||||||
|  | + | ||||||
|  | +    then: | ||||||
|  | +      required: | ||||||
|  | +        - reg | ||||||
|  |   | ||||||
|  |  additionalProperties: false | ||||||
|  |   | ||||||
|  |  examples: | ||||||
|  | +  - | | ||||||
|  | +    #include <dt-bindings/interrupt-controller/arm-gic.h> | ||||||
|  | +    // Example msm9860 based SoC (ipq8064): | ||||||
|  | +    gcc: clock-controller { | ||||||
|  | + | ||||||
|  | +           /* ... */ | ||||||
|  | + | ||||||
|  | +           tsens: thermal-sensor { | ||||||
|  | +                compatible = "qcom,ipq8064-tsens"; | ||||||
|  | + | ||||||
|  | +                 nvmem-cells = <&tsens_calib>, <&tsens_calib_backup>; | ||||||
|  | +                 nvmem-cell-names = "calib", "calib_backup"; | ||||||
|  | +                 interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>; | ||||||
|  | +                 interrupt-names = "uplow"; | ||||||
|  | + | ||||||
|  | +                 #qcom,sensors = <11>; | ||||||
|  | +                 #thermal-sensor-cells = <1>; | ||||||
|  | +          }; | ||||||
|  | +    }; | ||||||
|  | + | ||||||
|  |    - | | ||||||
|  |      #include <dt-bindings/interrupt-controller/arm-gic.h> | ||||||
|  |      // Example 1 (legacy: for pre v1 IP): | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | From 68e720ed73c8f038c8c500e4c49c1e65a993a448 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Tue, 6 Apr 2021 04:45:31 +0200 | ||||||
|  | Subject: [PATCH 10/10] drivers: thermal: tsens: Fix wrong slope on msm-8960 | ||||||
|  |  | ||||||
|  | Some user using some stats with the old legacy implementation and the | ||||||
|  | new implementation using the compute_intercept_slope reported an offset | ||||||
|  | of 3C. Fix the slope table to reflect the original temp. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | --- | ||||||
|  |  drivers/thermal/qcom/tsens-8960.c | 8 ++++---- | ||||||
|  |  1 file changed, 4 insertions(+), 4 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | index 58d09e927383..5cc5b3527f1f 100644 | ||||||
|  | --- a/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | +++ b/drivers/thermal/qcom/tsens-8960.c | ||||||
|  | @@ -45,11 +45,11 @@ | ||||||
|  |  #define S9_STATUS_OFF		0x3674 | ||||||
|  |  #define S10_STATUS_OFF		0x3678 | ||||||
|  |   | ||||||
|  | -/* Original slope - 200 to compensate mC to C inaccuracy */ | ||||||
|  | +/* Original slope - 350 to compensate mC to C inaccuracy */ | ||||||
|  |  static u32 tsens_msm8960_slope[] = { | ||||||
|  | -			976, 976, 954, 976, | ||||||
|  | -			911, 932, 932, 999, | ||||||
|  | -			932, 999, 932 | ||||||
|  | +			826, 826, 804, 826, | ||||||
|  | +			761, 782, 782, 849, | ||||||
|  | +			782, 849, 782 | ||||||
|  |  			}; | ||||||
|  |   | ||||||
|  |  static int suspend_8960(struct tsens_priv *priv) | ||||||
|  | --  | ||||||
|  | 2.30.2 | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user