diff options
Diffstat (limited to 'drivers/thermal/qcom/tsens.c')
| -rw-r--r-- | drivers/thermal/qcom/tsens.c | 165 | 
1 files changed, 133 insertions, 32 deletions
| diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index d8ce3a687b80..4c7ebd1d3f9c 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> @@ -85,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)/ @@ -515,6 +517,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 +541,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 +602,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 +629,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,25 +773,42 @@ 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,  							      priv->fields[i]); -			if (IS_ERR(priv->rf[i])) -				return PTR_ERR(priv->rf[i]); +			if (IS_ERR(priv->rf[i])) { +				ret = PTR_ERR(priv->rf[i]); +				goto err_put_device; +			}  		}  		ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);  		if (ret) @@ -769,6 +821,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 +847,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 +875,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 +913,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: @@ -895,6 +968,12 @@ 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,mdm9607-tsens", +		.data = &data_9607, +	}, {  		.compatible = "qcom,msm8916-tsens",  		.data = &data_8916,  	}, { @@ -943,10 +1022,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 +1063,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; | 
