mbox series

[v2,0/5] Add IPQ5332 TSENS support

Message ID 20230712113539.4029941-1-quic_ipkumar@quicinc.com
Headers show
Series Add IPQ5332 TSENS support | expand

Message

Praveenkumar I July 12, 2023, 11:35 a.m. UTC
IPQ5332 uses tsens v2.3.3 IP with combined interrupt for
upper/lower and critical. IPQ5332 does not have RPM and
kernel has to take care of TSENS enablement and calibration.
This patch series adds the sensor enablement and calibration
support. On top, adds IPQ5332 TSENS support.

[v2]:
	Dropped [v1 1/6] dt-bindings change and added nvmem-cell-names
	as part of [v2 2/5] ipq5332 dt-bindings

Praveenkumar I (5):
  thermal/drivers/tsens: Add TSENS enable and calibration support for V2
  dt-bindings: thermal: tsens: Add ipq5332 compatible
  arm64: dts: qcom: ipq5332: Add tsens node
  arm64: dts: qcom: ipq5332: Add thermal zone nodes
  thermal/drivers/tsens: Add IPQ5332 support

 .../bindings/thermal/qcom-tsens.yaml          |  12 ++
 arch/arm64/boot/dts/qcom/ipq5332.dtsi         | 144 +++++++++++++++
 drivers/thermal/qcom/tsens-v2.c               | 169 ++++++++++++++++++
 drivers/thermal/qcom/tsens.c                  |   5 +-
 drivers/thermal/qcom/tsens.h                  |   5 +-
 5 files changed, 333 insertions(+), 2 deletions(-)

Comments

Dmitry Baryshkov July 12, 2023, 12:22 p.m. UTC | #1
On 12/07/2023 14:35, Praveenkumar I wrote:
> SoCs without RPM have to enable sensors and calibrate from the kernel.
> Though TSENS IP supports 16 sensors, not all are used. So used hw_id
> to enable the relevant sensors.
> 
> Added new calibration function for V2 as the tsens.c calib function
> only supports V1.
> 
> Signed-off-by: Praveenkumar I <quic_ipkumar@quicinc.com>
> ---
> [v2]:
> 	Added separate init function for tsens v2 which calls init_common
> 	and initialize the remaining fields. Reformatted calibrate function
> 	and used hw_ids for sensors to enable.
> 
>   drivers/thermal/qcom/tsens-v2.c | 144 ++++++++++++++++++++++++++++++++
>   drivers/thermal/qcom/tsens.c    |   2 +-
>   drivers/thermal/qcom/tsens.h    |   3 +
>   3 files changed, 148 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
> index 29a61d2d6ca3..ba74d971fe95 100644
> --- a/drivers/thermal/qcom/tsens-v2.c
> +++ b/drivers/thermal/qcom/tsens-v2.c
> @@ -6,11 +6,23 @@
>   
>   #include <linux/bitops.h>
>   #include <linux/regmap.h>
> +#include <linux/nvmem-consumer.h>
>   #include "tsens.h"
>   
>   /* ----- SROT ------ */
>   #define SROT_HW_VER_OFF	0x0000
>   #define SROT_CTRL_OFF		0x0004
> +#define SROT_MEASURE_PERIOD	0x0008
> +#define SROT_Sn_CONVERSION	0x0060
> +#define V2_SHIFT_DEFAULT	0x0003
> +#define V2_SLOPE_DEFAULT	0x0cd0
> +#define V2_CZERO_DEFAULT	0x016a
> +#define ONE_PT_SLOPE		0x0cd0
> +#define TWO_PT_SHIFTED_GAIN	921600
> +#define ONE_PT_CZERO_CONST	94
> +#define SENSOR_CONVERSION(n)	(((n) * 4) + SROT_Sn_CONVERSION)
> +#define CONVERSION_SLOPE_SHIFT	10
> +#define CONVERSION_SHIFT_SHIFT	23
>   
>   /* ----- TM ------ */
>   #define TM_INT_EN_OFF			0x0004
> @@ -59,6 +71,11 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
>   	/* CTRL_OFF */
>   	[TSENS_EN]     = REG_FIELD(SROT_CTRL_OFF,    0,  0),
>   	[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF,    1,  1),
> +	[SENSOR_EN]    = REG_FIELD(SROT_CTRL_OFF,    3,  18),
> +	[CODE_OR_TEMP] = REG_FIELD(SROT_CTRL_OFF,    21, 21),
> +
> +	/* MAIN_MEASURE_PERIOD */
> +	[MAIN_MEASURE_PERIOD] = REG_FIELD(SROT_MEASURE_PERIOD, 0, 7),
>   
>   	/* ----- TM ------ */
>   	/* INTERRUPT ENABLE */
> @@ -104,6 +121,133 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
>   	[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
>   };
>   
> +static int tsens_v2_calibrate_sensor(struct device *dev, struct tsens_sensor *sensor,
> +				     struct regmap *map,  u32 mode, u32 base0, u32 base1)
> +{
> +	u32 slope, czero, val;
> +	char name[15];
> +	int ret;
> +
> +	/* Read offset value */
> +	ret = snprintf(name, sizeof(name), "s%d", sensor->hw_id);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = nvmem_cell_read_variable_le_u32(dev, name, &sensor->offset);
> +	if (ret)
> +		return ret;
> +
> +	/* Based on calib mode, program SHIFT, SLOPE and CZERO */
> +	switch (mode) {
> +	case TWO_PT_CALIB:
> +		slope = (TWO_PT_SHIFTED_GAIN / (base1 - base0));
> +
> +		czero = (base0 + sensor->offset - ((base1 - base0) / 3));
> +
> +		val = (V2_SHIFT_DEFAULT << CONVERSION_SHIFT_SHIFT) |
> +		      (slope << CONVERSION_SLOPE_SHIFT) | czero;
> +
> +		fallthrough;
> +	case ONE_PT_CALIB2:
> +		czero = base0 + sensor->offset - ONE_PT_CZERO_CONST;
> +
> +		val = (V2_SHIFT_DEFAULT << CONVERSION_SHIFT_SHIFT) |
> +		      (ONE_PT_SLOPE << CONVERSION_SLOPE_SHIFT) | czero;
> +
> +		break;
> +	default:
> +		dev_dbg(dev, "calibrationless mode\n");
> +
> +		val = (V2_SHIFT_DEFAULT << CONVERSION_SHIFT_SHIFT) |
> +		      (V2_SLOPE_DEFAULT << CONVERSION_SLOPE_SHIFT) | V2_CZERO_DEFAULT;
> +	}
> +
> +	regmap_write(map, SENSOR_CONVERSION(sensor->hw_id), val);
> +
> +	return 0;
> +}
> +
> +static int tsens_v2_calibration(struct tsens_priv *priv)
> +{
> +	struct device *dev = priv->dev;
> +	u32 mode, base0, base1;
> +	int i, ret;
> +
> +	if (priv->num_sensors > MAX_SENSORS)
> +		return -EINVAL;
> +
> +	ret = nvmem_cell_read_variable_le_u32(priv->dev, "mode", &mode);
> +	if (ret == -ENOENT)
> +		dev_warn(priv->dev, "Calibration data not present in DT\n");
> +	if (ret < 0)
> +		return ret;
> +
> +	dev_dbg(priv->dev, "calibration mode is %d\n", mode);
> +
> +	ret = nvmem_cell_read_variable_le_u32(priv->dev, "base0", &base0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = nvmem_cell_read_variable_le_u32(priv->dev, "base1", &base1);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Calibrate each sensor */
> +	for (i = 0; i < priv->num_sensors; i++) {
> +		ret = tsens_v2_calibrate_sensor(dev, &priv->sensor[i], priv->srot_map,
> +						mode, base0, base1);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init init_tsens_v2(struct tsens_priv *priv)
> +{
> +	int i, ret;
> +	u32 val = 0;
> +	struct device *dev = priv->dev;
> +
> +	ret = init_common(priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (priv->feat->ver_major != VER_2_X_NO_RPM)
> +		return 0;

No need to, you can rename the function to init_tsens_v2_no_rpm(h?) and 
use it just for non-rpm platforms.

> +
> +	priv->rf[CODE_OR_TEMP] = devm_regmap_field_alloc(dev, priv->srot_map,
> +							 priv->fields[CODE_OR_TEMP]);
> +	if (IS_ERR(priv->rf[CODE_OR_TEMP]))
> +		return PTR_ERR(priv->rf[CODE_OR_TEMP]);
> +
> +	priv->rf[MAIN_MEASURE_PERIOD] = devm_regmap_field_alloc(dev, priv->srot_map,
> +								priv->fields[MAIN_MEASURE_PERIOD]);
> +	if (IS_ERR(priv->rf[MAIN_MEASURE_PERIOD]))
> +		return PTR_ERR(priv->rf[MAIN_MEASURE_PERIOD]);
> +
> +	regmap_field_write(priv->rf[TSENS_SW_RST], 0x1);
> +
> +	/* Update measure period to 2ms */
> +	regmap_field_write(priv->rf[MAIN_MEASURE_PERIOD], 0x1);
> +
> +	/* Enable available sensors */
> +	for (i = 0; i < priv->num_sensors; i++)
> +		val |= 1 << priv->sensor[i].hw_id;
> +
> +	regmap_field_write(priv->rf[SENSOR_EN], val);
> +
> +	/* Real temperature format */
> +	regmap_field_write(priv->rf[CODE_OR_TEMP], 0x1);
> +
> +	regmap_field_write(priv->rf[TSENS_SW_RST], 0x0);
> +
> +	/* Enable TSENS */
> +	regmap_field_write(priv->rf[TSENS_EN], 0x1);
> +
> +	return 0;
> +}
> +
>   static const struct tsens_ops ops_generic_v2 = {
>   	.init		= init_common,
>   	.get_temp	= get_temp_tsens_valid,
> diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
> index 98c356acfe98..5d2ad3b155ec 100644
> --- a/drivers/thermal/qcom/tsens.c
> +++ b/drivers/thermal/qcom/tsens.c
> @@ -974,7 +974,7 @@ int __init init_common(struct tsens_priv *priv)
>   	ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
>   	if (ret)
>   		goto err_put_device;
> -	if (!enabled) {
> +	if (!enabled && !VER_2_X_NO_RPM) {

You probably meant something else here. `!const' is going to evaluate to 
false.

>   		dev_err(dev, "%s: device not enabled\n", __func__);
>   		ret = -ENODEV;
>   		goto err_put_device;
> diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
> index 2805de1c6827..b2e8f0f2b466 100644
> --- a/drivers/thermal/qcom/tsens.h
> +++ b/drivers/thermal/qcom/tsens.h
> @@ -35,6 +35,7 @@ enum tsens_ver {
>   	VER_0_1,
>   	VER_1_X,
>   	VER_2_X,
> +	VER_2_X_NO_RPM,
>   };
>   
>   enum tsens_irq_type {
> @@ -168,6 +169,8 @@ enum regfield_ids {
>   	TSENS_SW_RST,
>   	SENSOR_EN,
>   	CODE_OR_TEMP,
> +	/* MEASURE_PERIOD */
> +	MAIN_MEASURE_PERIOD,
>   
>   	/* ----- TM ------ */
>   	/* TRDY */
Krzysztof Kozlowski July 12, 2023, 2:25 p.m. UTC | #2
On 12/07/2023 13:35, Praveenkumar I wrote:
> IPQ5332 uses TSENS v2.3.3 with combined interrupt. RPM is not
> available in the SoC, hence adding new compatible to have the
> sensor enablement and calibration function.
> 
> This patch also adds nvmem-cell-names for ipq5332
> 
> Signed-off-by: Praveenkumar I <quic_ipkumar@quicinc.com>
> ---
> [v2]:
> 	Followed the order for ipq5332 and added nvmem-cell-names.
> 
>  .../devicetree/bindings/thermal/qcom-tsens.yaml      | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
> index 27e9e16e6455..cca115906762 100644
> --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
> +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
> @@ -69,6 +69,7 @@ properties:
>  
>        - description: v2 of TSENS with combined interrupt
>          enum:
> +          - qcom,ipq5332-tsens
>            - qcom,ipq8074-tsens
>  
>        - description: v2 of TSENS with combined interrupt
> @@ -205,6 +206,15 @@ properties:
>            - const: s9_p2_backup
>            - const: s10_p1_backup
>            - const: s10_p2_backup
> +      - items:
> +          - const: mode
> +          - const: base0
> +          - const: base1
> +          - pattern: '^s[0-9]+$'
> +          - pattern: '^s[0-9]+$'
> +          - pattern: '^s[0-9]+$'
> +          - pattern: '^s[0-9]+$'
> +          - pattern: '^s[0-9]+$'

Previously there were 17 items here. What changed?

Best regards,
Krzysztof