}
/**
- * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
+ * tsens_read_temp - Retrieve temperature readings from the hardware.
* @s: Pointer to sensor struct
* @field: Index into regmap_field array pointing to temperature data
+ * @temp: temperature in deciCelsius to be read from hardware
+ *
+ * This function handles temperature returned in ADC code or deciCelsius
+ * depending on IP version.
+ *
+ * Return: 0 on success, a negative errno will be returned in error cases
+ */
+static int tsens_read_temp(const struct tsens_sensor *s, int field, int *temp)
+{
+ struct tsens_priv *priv = s->priv;
+ int temp_val[MAX_READ_RETRY] = {0};
+ u32 status;
+ int ret;
+ u32 last_temp_mask = GENMASK(priv->fields[LAST_TEMP_0].msb,
+ priv->fields[LAST_TEMP_0].lsb);
+ u32 valid_bit = priv->rf[VALID_0] ? BIT(priv->fields[VALID_0].lsb) : 0;
+
+ for (int i = 0; i < MAX_READ_RETRY; i++) {
+ ret = regmap_read(priv->tm_map, priv->fields[field].reg, &status);
+ if (ret)
+ return ret;
+
+ /* VER_0 doesn't have a VALID bit */
+ if (!valid_bit) {
+ *temp = status & last_temp_mask;
+ return 0;
+ }
+
+ temp_val[i] = status & last_temp_mask;
+
+ if (status & valid_bit) {
+ *temp = temp_val[i];
+ return 0;
+ }
+ }
+
+ /*
+ * As per the HW guidelines, if none of the attempts observe a
+ * valid sample, a stable fallback value must be returned. If the
+ * first and second samples match, the second value is returned;
+ * otherwise, if the second and third samples match, the third
+ * value is returned.
+ */
+ if (temp_val[0] == temp_val[1])
+ *temp = temp_val[1];
+ else if (temp_val[1] == temp_val[2])
+ *temp = temp_val[2];
+ else
+ return -EAGAIN;
+
+ return 0;
+}
+
+/**
+ * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
+ * @s: Pointer to sensor struct
+ * @temp: temperature in milliCelsius to be read from hardware
*
* This function handles temperature returned in ADC code or deciCelsius
* depending on IP version.
* Return: Temperature in milliCelsius on success, a negative errno will
* be returned in error cases
*/
-static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
+static int tsens_hw_to_mC(const struct tsens_sensor *s, int temp)
{
struct tsens_priv *priv = s->priv;
u32 resolution;
- u32 temp = 0;
- int ret;
resolution = priv->fields[LAST_TEMP_0].msb -
priv->fields[LAST_TEMP_0].lsb;
- ret = regmap_field_read(priv->rf[field], &temp);
- if (ret)
- return ret;
-
/* Convert temperature from ADC code to milliCelsius */
if (priv->feat->adc)
return code_to_degc(temp, s) * 1000;
&d->crit_irq_mask);
if (ret)
return ret;
-
- d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
+ ret = regmap_field_read(priv->rf[CRIT_THRESH_0 + hw_id], &d->crit_thresh);
+ if (ret)
+ return ret;
+ d->crit_thresh = tsens_hw_to_mC(s, d->crit_thresh);
} else {
/* No mask register on older TSENS */
d->up_irq_mask = 0;
d->crit_thresh = 0;
}
- d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
- d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
+ ret = regmap_field_read(priv->rf[UP_THRESH_0 + hw_id], &d->up_thresh);
+ if (ret)
+ return ret;
+
+ d->up_thresh = tsens_hw_to_mC(s, d->up_thresh);
+ ret = regmap_field_read(priv->rf[LOW_THRESH_0 + hw_id], &d->low_thresh);
+ if (ret)
+ return ret;
+
+ d->low_thresh = tsens_hw_to_mC(s, d->low_thresh);
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
hw_id, __func__,
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
{
- struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id;
u32 temp_idx = LAST_TEMP_0 + hw_id;
- u32 valid_idx = VALID_0 + hw_id;
- u32 valid;
int ret;
- /* VER_0 doesn't have VALID bit */
- if (tsens_version(priv) == VER_0)
- goto get_temp;
-
- /* 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.
- * Wait 1 us since it's the min of poll_timeout macro.
- * Old value was 400 ns.
- */
- ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid,
- valid, 1, 20 * USEC_PER_MSEC);
- if (ret)
- return ret;
-
-get_temp:
- /* Valid bit is set, OK to read the temperature */
- *temp = tsens_hw_to_mC(s, temp_idx);
+ ret = tsens_read_temp(s, temp_idx, temp);
+ if (!ret)
+ *temp = tsens_hw_to_mC(s, *temp);
- return 0;
+ return ret;
}
int get_temp_common(const struct tsens_sensor *s, int *temp)