diff mbox series

[v3,4/8] iio: magnetometer: yas530: Correct temperature handling

Message ID 076e87f66378be8c729723cb9be5bc9151c081ab.1655509425.git.jahau@rocketmail.com (mailing list archive)
State Changes Requested
Headers show
Series Add support for magnetometer Yamaha YAS537 | expand

Commit Message

Jakob Hauser June 18, 2022, 12:13 a.m. UTC
The raw temperature value is a number of counts from a certain starting
point. The resolution of the temperature counts is different for the YAS
variants.

Temperature compensation for YAS532 version AC seems to be handled differently.
It uses the deviation from 20 degree Celsius [1] whereas YAS530 and older
versions of YAS532 apply solely the t value as a multiplier [2][3].

In funtion yas5xx_read_raw(), add case IIO_CHAN_INFO_PROCESSED. Remove scale
of temperature as this isn't applied.

Additionally correct sign of temperature channel in iio_chan_spec. It's already
defined that way in the yas5xx_get_measure() function.

[1] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c#L442
[2] https://github.com/NovaFusion/android_kernel_samsung_golden/blob/cm-12.1/drivers/sensor/compass/yas_mag_driver-yas530.c#L881-L883
[3] https://github.com/LineageOS/android_kernel_samsung_msm8930-common/blob/lineage-18.1/drivers/sensors/geomagnetic/yas_mag_driver-yas53x.c#L856-L858

Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Jakob Hauser <jahau@rocketmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
 drivers/iio/magnetometer/yamaha-yas530.c | 99 ++++++++++++++++++------
 1 file changed, 76 insertions(+), 23 deletions(-)

Comments

Jonathan Cameron June 18, 2022, 2:53 p.m. UTC | #1
On Sat, 18 Jun 2022 02:13:12 +0200
Jakob Hauser <jahau@rocketmail.com> wrote:

> The raw temperature value is a number of counts from a certain starting
> point. The resolution of the temperature counts is different for the YAS
> variants.
> 
> Temperature compensation for YAS532 version AC seems to be handled differently.
> It uses the deviation from 20 degree Celsius [1] whereas YAS530 and older
> versions of YAS532 apply solely the t value as a multiplier [2][3].
> 
> In funtion yas5xx_read_raw(), add case IIO_CHAN_INFO_PROCESSED. Remove scale
> of temperature as this isn't applied.
> 
> Additionally correct sign of temperature channel in iio_chan_spec. It's already
> defined that way in the yas5xx_get_measure() function.
> 
> [1] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c#L442
> [2] https://github.com/NovaFusion/android_kernel_samsung_golden/blob/cm-12.1/drivers/sensor/compass/yas_mag_driver-yas530.c#L881-L883
> [3] https://github.com/LineageOS/android_kernel_samsung_msm8930-common/blob/lineage-18.1/drivers/sensors/geomagnetic/yas_mag_driver-yas53x.c#L856-L858
> 
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Jakob Hauser <jahau@rocketmail.com>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
> ---
>  drivers/iio/magnetometer/yamaha-yas530.c | 99 ++++++++++++++++++------
>  1 file changed, 76 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c
> index bd43b2555b73..9b45a550f31e 100644
> --- a/drivers/iio/magnetometer/yamaha-yas530.c
> +++ b/drivers/iio/magnetometer/yamaha-yas530.c
> @@ -77,6 +77,7 @@
>  #define YAS530_DATA_BITS		12
>  #define YAS530_DATA_CENTER		BIT(YAS530_DATA_BITS - 1)
>  #define YAS530_DATA_OVERFLOW		(BIT(YAS530_DATA_BITS) - 1)
> +#define YAS530_20DEGREES		182 /* Counts starting at -62 °C */
>  
>  #define YAS532_DEVICE_ID		0x02 /* YAS532/YAS533 (MS-3R/F) */
>  #define YAS532_VERSION_AB		0 /* YAS532/533 AB (MS-3R/F AB) */
> @@ -88,7 +89,7 @@
>  #define YAS532_DATA_BITS		13
>  #define YAS532_DATA_CENTER		BIT(YAS532_DATA_BITS - 1)
>  #define YAS532_DATA_OVERFLOW		(BIT(YAS532_DATA_BITS) - 1)
> -#define YAS532_20DEGREES		390 /* Looks like Kelvin */
> +#define YAS532_20DEGREES		390 /* Counts starting at -50 °C */
>  
>  /* These variant IDs are known from code dumps */
>  #define YAS537_DEVICE_ID		0x07 /* YAS537 (MS-3T) */
> @@ -314,7 +315,7 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)

Hmm. I'm not a great fun of big hydra functions to handle differences
between devices.  This could easily all be one code flow with some
lookups into chip specific constant data (as btw could a lot of
the other switch statements in the existing driver).


>  static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
>  {
>  	struct yas5xx_calibration *c = &yas5xx->calibration;
> -	u16 t, x, y1, y2;
> +	u16 t_ref, t, x, y1, y2;
>  	/* These are "signed x, signed y1 etc */
>  	s32 sx, sy1, sy2, sy, sz;
>  	int ret;
> @@ -329,16 +330,46 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
>  	sy1 = yas5xx_linearize(yas5xx, y1, 1);
>  	sy2 = yas5xx_linearize(yas5xx, y2, 2);
>  
> -	/*
> -	 * Temperature compensation for x, y1, y2 respectively:
> -	 *
> -	 *          Cx * t
> -	 * x' = x - ------
> -	 *           100
> -	 */
> -	sx = sx - (c->Cx * t) / 100;
> -	sy1 = sy1 - (c->Cy1 * t) / 100;
> -	sy2 = sy2 - (c->Cy2 * t) / 100;
> +	/* Set the temperature reference value (unit: counts) */
> +	switch (yas5xx->devid) {
> +	case YAS530_DEVICE_ID:
> +		t_ref = YAS530_20DEGREES;

One thought to simplify the divergent flow below.

		t_ref2 = 0;
> +		break;
> +	case YAS532_DEVICE_ID:
> +		t_ref = YAS532_20DEGREES;
		if (yas5xx->version == YAS532_VERSION_AC)
			t_ref2 = YAS432_20DEGREES;
		else
			t_ref2 = 0;

Possibly with moving some of the comments below up here.
As mentioned below, this stuff would be even better if
in a chip type specific const structure rather than as code.
That is have one switch statement in probe that picks from
an array of 

struct yas5xx_chip_info {
	/* COMMENTS ON WHAT these are.. *
	u16 tref;
	u16 tref2;
	int ref_temp_celsius;
	int min_temp_celsuis;
};
static const struct yas5xx_chip_info[] = {
	[ENUM value we will use to pick right on in probe] = {
		...

etc


> +		break;
> +	default:
> +		dev_err(yas5xx->dev, "unknown device type\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Temperature compensation for x, y1, y2 respectively */
> +	if (yas5xx->devid == YAS532_DEVICE_ID &&
> +	    yas5xx->version == YAS532_VERSION_AC) {
> +		/*
> +		 * YAS532 version AC uses the temperature deviation as a
> +		 * multiplier.
> +		 *
> +		 *          Cx * (t - t_ref)
> +		 * x' = x - ----------------
> +		 *                100
> +		 */
> +		sx = sx - (c->Cx * (t - t_ref)) / 100;
> +		sy1 = sy1 - (c->Cy1 * (t - t_ref)) / 100;
> +		sy2 = sy2 - (c->Cy2 * (t - t_ref)) / 100;

Use t_ref2 here and then you could drop the two paths.

> +	} else {
> +		/*
> +		 * YAS530 and YAS532 version AB use solely the t value as a
> +		 * multiplier.
> +		 *
> +		 *          Cx * t
> +		 * x' = x - ------
> +		 *           100
> +		 */
> +		sx = sx - (c->Cx * t) / 100;
> +		sy1 = sy1 - (c->Cy1 * t) / 100;
> +		sy2 = sy2 - (c->Cy2 * t) / 100;
> +	}
>  
>  	/*
>  	 * Break y1 and y2 into y and z, y1 and y2 are apparently encoding
> @@ -347,11 +378,37 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
>  	sy = sy1 - sy2;
>  	sz = -sy1 - sy2;
>  
> -	/*
> -	 * FIXME: convert to Celsius? Just guessing this is given
> -	 * as 1/10:s of degrees so multiply by 100 to get millicentigrades.
> -	 */
> -	*to = t * 100;
> +	/* Process temperature readout */
> +	switch (yas5xx->devid) {
> +	case YAS530_DEVICE_ID:
> +		/*
> +		 * Raw temperature value t is the number of counts starting
> +		 * at -62 °C. Reference value t_ref is the number of counts
> +		 * between -62 °C and 20 °C (82 °C range).
Roll this info into the maths and only have the constants set in the switch
statement.  Even better if you just move them into chip specific data so
look them up directly rather than via a switch of devid.  The whole driver
would benefit from moving this stuff to const data rather than switch
statements all over the place.

	int min_temp_x10 = yas5xx->chip_info.min_temp_x10;
	const int ref_temp_x10 = 200;

	*to = (min_temp_x10 + ((ref_temp_x10 - min_temp_x10) * t / t_ref)) * 100;

That should make the code self explanatory and remove need for the comments.

> +		 *
> +		 * Temperature in °C would be (82 / t_ref * t) - 62.
> +		 *
> +		 * Contrary to this, perform multiplication first and division
> +		 * second due to calculating with integers.
> +		 *
> +		 * To get a nicer result, calculate with 1/10:s degrees Celsius
> +		 * and finally multiply by 100 to return milli degrees Celsius.
> +		 */
> +		*to = ((820 * t / t_ref) - 620) * 100;
> +		break;
> +	case YAS532_DEVICE_ID:
> +		/*
> +		 * Actually same procedure for YAS532 but the starting point is
> +		 * at -50 °C. Reference value t_ref is the number of counts
> +		 * between -50 °C and 20 °C (70 °C range).
> +		 */
> +		*to = ((700 * t / t_ref) - 500) * 100;
> +		break;
> +	default:
> +		dev_err(yas5xx->dev, "unknown device type\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Calibrate [x,y,z] with some formulas like this:
>  	 *
> @@ -384,6 +441,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev,
>  	int ret;
>  
>  	switch (mask) {
> +	case IIO_CHAN_INFO_PROCESSED:
>  	case IIO_CHAN_INFO_RAW:
>  		pm_runtime_get_sync(yas5xx->dev);
>  		ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z);
> @@ -410,11 +468,6 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev,
>  		}
>  		return IIO_VAL_INT;
>  	case IIO_CHAN_INFO_SCALE:
> -		if (chan->address == 0) {
> -			/* Temperature is unscaled */
> -			*val = 1;
> -			return IIO_VAL_INT;
> -		}
>  		switch (yas5xx->devid) {
>  		case YAS530_DEVICE_ID:
>  			/*
> @@ -513,7 +566,7 @@ static const struct iio_chan_spec yas5xx_channels[] = {
>  		.address = 0,
>  		.scan_index = 0,
>  		.scan_type = {
> -			.sign = 'u',
> +			.sign = 's',
>  			.realbits = 32,
>  			.storagebits = 32,
>  			.endianness = IIO_CPU,
Jakob Hauser June 21, 2022, 12:48 a.m. UTC | #2
Hi Jonathan,

On 18.06.22 16:53, Jonathan Cameron wrote:
>
> On Sat, 18 Jun 2022 02:13:12 +0200
> Jakob Hauser <jahau@rocketmail.com> wrote:
...
>>  /* These variant IDs are known from code dumps */
>>  #define YAS537_DEVICE_ID		0x07 /* YAS537 (MS-3T) */
>> @@ -314,7 +315,7 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
> 
> Hmm. I'm not a great fun of big hydra functions to handle differences
> between devices.  This could easily all be one code flow with some
> lookups into chip specific constant data (as btw could a lot of
> the other switch statements in the existing driver).

I'll try to implement the chip_info approach. This should become a
separate patch.

Concerning the patchset, I would prefer to introduce the chip_info
approach rather late. That would mean to leave this patch unchanged and
introduce your suggestions later within the patchset. I think it's
easier to follow the changes along the patchset.

However, you probably would prefer to place the chip_info patch rather
early in the patchset?

>>  static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
>>  {
>>  	struct yas5xx_calibration *c = &yas5xx->calibration;
>> -	u16 t, x, y1, y2;
>> +	u16 t_ref, t, x, y1, y2;
>>  	/* These are "signed x, signed y1 etc */
>>  	s32 sx, sy1, sy2, sy, sz;
>>  	int ret;
>> @@ -329,16 +330,46 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
>>  	sy1 = yas5xx_linearize(yas5xx, y1, 1);
>>  	sy2 = yas5xx_linearize(yas5xx, y2, 2);
>>  
>> -	/*
>> -	 * Temperature compensation for x, y1, y2 respectively:
>> -	 *
>> -	 *          Cx * t
>> -	 * x' = x - ------
>> -	 *           100
>> -	 */
>> -	sx = sx - (c->Cx * t) / 100;
>> -	sy1 = sy1 - (c->Cy1 * t) / 100;
>> -	sy2 = sy2 - (c->Cy2 * t) / 100;
>> +	/* Set the temperature reference value (unit: counts) */
>> +	switch (yas5xx->devid) {
>> +	case YAS530_DEVICE_ID:
>> +		t_ref = YAS530_20DEGREES;
> 
> One thought to simplify the divergent flow below.
> 
> 		t_ref2 = 0;
>> +		break;
>> +	case YAS532_DEVICE_ID:
>> +		t_ref = YAS532_20DEGREES;
> 		if (yas5xx->version == YAS532_VERSION_AC)
> 			t_ref2 = YAS432_20DEGREES;
> 		else
> 			t_ref2 = 0;

The t_ref2 approach looks confusing to me. Because for the most version
it's "t_ref2 = 0", only one version out of four needs this.

Another approach: I would rather introduce t_comp (for compensation). In
the chip_info, for the most version it would be...

        .t_comp = t,

... and for the one variant it would be:

        .t_comp = t - t_ref,

A problem: I would include the YAS variants like YAS530, YAS532 etc. in
the chip_info. The versions like "AB" and "AC", on the other hand, I
wouldn't include into the chip_info, instead I would handle these in the
functions. In that case the, "t_comp" thing would need to be done in the
function using an if statement, similar to what you suggested up here.
I'll have a closer look when setting up patchset v4.

> Possibly with moving some of the comments below up here.
> As mentioned below, this stuff would be even better if
> in a chip type specific const structure rather than as code.
> That is have one switch statement in probe that picks from
> an array of 
> 
> struct yas5xx_chip_info {
> 	/* COMMENTS ON WHAT these are.. *
> 	u16 tref;
> 	u16 tref2;
> 	int ref_temp_celsius;
> 	int min_temp_celsuis;
> };
> static const struct yas5xx_chip_info[] = {
> 	[ENUM value we will use to pick right on in probe] = {
> 		...
> 
> etc

Thanks for writing down what it's supposed to look, that's helpful to
compare with other examples.

...

>> @@ -347,11 +378,37 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
>>  	sy = sy1 - sy2;
>>  	sz = -sy1 - sy2;
>>  
>> -	/*
>> -	 * FIXME: convert to Celsius? Just guessing this is given
>> -	 * as 1/10:s of degrees so multiply by 100 to get millicentigrades.
>> -	 */
>> -	*to = t * 100;
>> +	/* Process temperature readout */
>> +	switch (yas5xx->devid) {
>> +	case YAS530_DEVICE_ID:
>> +		/*
>> +		 * Raw temperature value t is the number of counts starting
>> +		 * at -62 °C. Reference value t_ref is the number of counts
>> +		 * between -62 °C and 20 °C (82 °C range).
> Roll this info into the maths and only have the constants set in the switch
> statement.  Even better if you just move them into chip specific data so
> look them up directly rather than via a switch of devid.  The whole driver
> would benefit from moving this stuff to const data rather than switch
> statements all over the place.
> 
> 	int min_temp_x10 = yas5xx->chip_info.min_temp_x10;
> 	const int ref_temp_x10 = 200;
> 
> 	*to = (min_temp_x10 + ((ref_temp_x10 - min_temp_x10) * t / t_ref)) * 100;
> 
> That should make the code self explanatory and remove need for the comments.

I'll have a look and will try to implement this.

...

Kind regards,
Jakob
Jonathan Cameron June 25, 2022, 2:14 p.m. UTC | #3
On Tue, 21 Jun 2022 02:48:46 +0200
Jakob Hauser <jahau@rocketmail.com> wrote:

> Hi Jonathan,
> 
> On 18.06.22 16:53, Jonathan Cameron wrote:
> >
> > On Sat, 18 Jun 2022 02:13:12 +0200
> > Jakob Hauser <jahau@rocketmail.com> wrote:  
> ...
> >>  /* These variant IDs are known from code dumps */
> >>  #define YAS537_DEVICE_ID		0x07 /* YAS537 (MS-3T) */
> >> @@ -314,7 +315,7 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)  
> > 
> > Hmm. I'm not a great fun of big hydra functions to handle differences
> > between devices.  This could easily all be one code flow with some
> > lookups into chip specific constant data (as btw could a lot of
> > the other switch statements in the existing driver).  
> 
> I'll try to implement the chip_info approach. This should become a
> separate patch.
> 
> Concerning the patchset, I would prefer to introduce the chip_info
> approach rather late. That would mean to leave this patch unchanged and
> introduce your suggestions later within the patchset. I think it's
> easier to follow the changes along the patchset.
> 
> However, you probably would prefer to place the chip_info patch rather
> early in the patchset?

Whilst I'd prefer it earlier, if it's a real pain, just put a note on
that in the cover letter and I'll cope :)

> 
> >>  static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
> >>  {
> >>  	struct yas5xx_calibration *c = &yas5xx->calibration;
> >> -	u16 t, x, y1, y2;
> >> +	u16 t_ref, t, x, y1, y2;
> >>  	/* These are "signed x, signed y1 etc */
> >>  	s32 sx, sy1, sy2, sy, sz;
> >>  	int ret;
> >> @@ -329,16 +330,46 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
> >>  	sy1 = yas5xx_linearize(yas5xx, y1, 1);
> >>  	sy2 = yas5xx_linearize(yas5xx, y2, 2);
> >>  
> >> -	/*
> >> -	 * Temperature compensation for x, y1, y2 respectively:
> >> -	 *
> >> -	 *          Cx * t
> >> -	 * x' = x - ------
> >> -	 *           100
> >> -	 */
> >> -	sx = sx - (c->Cx * t) / 100;
> >> -	sy1 = sy1 - (c->Cy1 * t) / 100;
> >> -	sy2 = sy2 - (c->Cy2 * t) / 100;
> >> +	/* Set the temperature reference value (unit: counts) */
> >> +	switch (yas5xx->devid) {
> >> +	case YAS530_DEVICE_ID:
> >> +		t_ref = YAS530_20DEGREES;  
> > 
> > One thought to simplify the divergent flow below.
> > 
> > 		t_ref2 = 0;  
> >> +		break;
> >> +	case YAS532_DEVICE_ID:
> >> +		t_ref = YAS532_20DEGREES;  
> > 		if (yas5xx->version == YAS532_VERSION_AC)
> > 			t_ref2 = YAS432_20DEGREES;
> > 		else
> > 			t_ref2 = 0;  
> 
> The t_ref2 approach looks confusing to me. Because for the most version
> it's "t_ref2 = 0", only one version out of four needs this.
> 
> Another approach: I would rather introduce t_comp (for compensation). In
> the chip_info, for the most version it would be...
> 
>         .t_comp = t,
> 
> ... and for the one variant it would be:
> 
>         .t_comp = t - t_ref,

That looks sensible to me.
> 
> A problem: I would include the YAS variants like YAS530, YAS532 etc. in
> the chip_info. The versions like "AB" and "AC", on the other hand, I
> wouldn't include into the chip_info, instead I would handle these in the
> functions. In that case the, "t_comp" thing would need to be done in the
> function using an if statement, similar to what you suggested up here.

I'd assume there won't be too many different versions that need separate
support and have a chipinfo for each of those versions.  So you
select the chipinfo based on both the device part number and version.

>
Thanks,

J
diff mbox series

Patch

diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c
index bd43b2555b73..9b45a550f31e 100644
--- a/drivers/iio/magnetometer/yamaha-yas530.c
+++ b/drivers/iio/magnetometer/yamaha-yas530.c
@@ -77,6 +77,7 @@ 
 #define YAS530_DATA_BITS		12
 #define YAS530_DATA_CENTER		BIT(YAS530_DATA_BITS - 1)
 #define YAS530_DATA_OVERFLOW		(BIT(YAS530_DATA_BITS) - 1)
+#define YAS530_20DEGREES		182 /* Counts starting at -62 °C */
 
 #define YAS532_DEVICE_ID		0x02 /* YAS532/YAS533 (MS-3R/F) */
 #define YAS532_VERSION_AB		0 /* YAS532/533 AB (MS-3R/F AB) */
@@ -88,7 +89,7 @@ 
 #define YAS532_DATA_BITS		13
 #define YAS532_DATA_CENTER		BIT(YAS532_DATA_BITS - 1)
 #define YAS532_DATA_OVERFLOW		(BIT(YAS532_DATA_BITS) - 1)
-#define YAS532_20DEGREES		390 /* Looks like Kelvin */
+#define YAS532_20DEGREES		390 /* Counts starting at -50 °C */
 
 /* These variant IDs are known from code dumps */
 #define YAS537_DEVICE_ID		0x07 /* YAS537 (MS-3T) */
@@ -314,7 +315,7 @@  static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
 static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
 {
 	struct yas5xx_calibration *c = &yas5xx->calibration;
-	u16 t, x, y1, y2;
+	u16 t_ref, t, x, y1, y2;
 	/* These are "signed x, signed y1 etc */
 	s32 sx, sy1, sy2, sy, sz;
 	int ret;
@@ -329,16 +330,46 @@  static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
 	sy1 = yas5xx_linearize(yas5xx, y1, 1);
 	sy2 = yas5xx_linearize(yas5xx, y2, 2);
 
-	/*
-	 * Temperature compensation for x, y1, y2 respectively:
-	 *
-	 *          Cx * t
-	 * x' = x - ------
-	 *           100
-	 */
-	sx = sx - (c->Cx * t) / 100;
-	sy1 = sy1 - (c->Cy1 * t) / 100;
-	sy2 = sy2 - (c->Cy2 * t) / 100;
+	/* Set the temperature reference value (unit: counts) */
+	switch (yas5xx->devid) {
+	case YAS530_DEVICE_ID:
+		t_ref = YAS530_20DEGREES;
+		break;
+	case YAS532_DEVICE_ID:
+		t_ref = YAS532_20DEGREES;
+		break;
+	default:
+		dev_err(yas5xx->dev, "unknown device type\n");
+		return -EINVAL;
+	}
+
+	/* Temperature compensation for x, y1, y2 respectively */
+	if (yas5xx->devid == YAS532_DEVICE_ID &&
+	    yas5xx->version == YAS532_VERSION_AC) {
+		/*
+		 * YAS532 version AC uses the temperature deviation as a
+		 * multiplier.
+		 *
+		 *          Cx * (t - t_ref)
+		 * x' = x - ----------------
+		 *                100
+		 */
+		sx = sx - (c->Cx * (t - t_ref)) / 100;
+		sy1 = sy1 - (c->Cy1 * (t - t_ref)) / 100;
+		sy2 = sy2 - (c->Cy2 * (t - t_ref)) / 100;
+	} else {
+		/*
+		 * YAS530 and YAS532 version AB use solely the t value as a
+		 * multiplier.
+		 *
+		 *          Cx * t
+		 * x' = x - ------
+		 *           100
+		 */
+		sx = sx - (c->Cx * t) / 100;
+		sy1 = sy1 - (c->Cy1 * t) / 100;
+		sy2 = sy2 - (c->Cy2 * t) / 100;
+	}
 
 	/*
 	 * Break y1 and y2 into y and z, y1 and y2 are apparently encoding
@@ -347,11 +378,37 @@  static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
 	sy = sy1 - sy2;
 	sz = -sy1 - sy2;
 
-	/*
-	 * FIXME: convert to Celsius? Just guessing this is given
-	 * as 1/10:s of degrees so multiply by 100 to get millicentigrades.
-	 */
-	*to = t * 100;
+	/* Process temperature readout */
+	switch (yas5xx->devid) {
+	case YAS530_DEVICE_ID:
+		/*
+		 * Raw temperature value t is the number of counts starting
+		 * at -62 °C. Reference value t_ref is the number of counts
+		 * between -62 °C and 20 °C (82 °C range).
+		 *
+		 * Temperature in °C would be (82 / t_ref * t) - 62.
+		 *
+		 * Contrary to this, perform multiplication first and division
+		 * second due to calculating with integers.
+		 *
+		 * To get a nicer result, calculate with 1/10:s degrees Celsius
+		 * and finally multiply by 100 to return milli degrees Celsius.
+		 */
+		*to = ((820 * t / t_ref) - 620) * 100;
+		break;
+	case YAS532_DEVICE_ID:
+		/*
+		 * Actually same procedure for YAS532 but the starting point is
+		 * at -50 °C. Reference value t_ref is the number of counts
+		 * between -50 °C and 20 °C (70 °C range).
+		 */
+		*to = ((700 * t / t_ref) - 500) * 100;
+		break;
+	default:
+		dev_err(yas5xx->dev, "unknown device type\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Calibrate [x,y,z] with some formulas like this:
 	 *
@@ -384,6 +441,7 @@  static int yas5xx_read_raw(struct iio_dev *indio_dev,
 	int ret;
 
 	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
 	case IIO_CHAN_INFO_RAW:
 		pm_runtime_get_sync(yas5xx->dev);
 		ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z);
@@ -410,11 +468,6 @@  static int yas5xx_read_raw(struct iio_dev *indio_dev,
 		}
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
-		if (chan->address == 0) {
-			/* Temperature is unscaled */
-			*val = 1;
-			return IIO_VAL_INT;
-		}
 		switch (yas5xx->devid) {
 		case YAS530_DEVICE_ID:
 			/*
@@ -513,7 +566,7 @@  static const struct iio_chan_spec yas5xx_channels[] = {
 		.address = 0,
 		.scan_index = 0,
 		.scan_type = {
-			.sign = 'u',
+			.sign = 's',
 			.realbits = 32,
 			.storagebits = 32,
 			.endianness = IIO_CPU,