cell = nvmem_cell_get(dev, cname); if (IS_ERR(cell)) return ERR_CAST(cell);
ret = nvmem_cell_read(cell, &data);
nvmem_cell_put(cell);
return ret;
}
int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup)
{
u32 mode;
u32 base1, base2; char name[] = "sXX_pY_backup"; /* s10_p1_backup */ int i, ret;
if (priv->num_sensors > MAX_SENSORS) return -EINVAL;
ret = snprintf(name, sizeof(name), "mode%s", backup ? "_backup" : ""); if (ret < 0) return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &mode); if (ret == -ENOENT)
dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n"); if (ret < 0) return ret;
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
ret = snprintf(name, sizeof(name), "base1%s", backup ? "_backup" : ""); if (ret < 0) return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base1); if (ret < 0) return ret;
ret = snprintf(name, sizeof(name), "base2%s", backup ? "_backup" : ""); if (ret < 0) return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base2); if (ret < 0) return ret;
for (i = 0; i < priv->num_sensors; i++) {
ret = snprintf(name, sizeof(name), "s%d_p1%s", priv->sensor[i].hw_id,
backup ? "_backup" : ""); if (ret < 0) return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p1[i]); if (ret) return ret;
ret = snprintf(name, sizeof(name), "s%d_p2%s", priv->sensor[i].hw_id,
backup ? "_backup" : ""); if (ret < 0) return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p2[i]); if (ret) return ret;
}
switch (mode) { case ONE_PT_CALIB: for (i = 0; i < priv->num_sensors; i++)
p1[i] = p1[i] + (base1 << shift); break; case TWO_PT_CALIB: case TWO_PT_CALIB_NO_OFFSET: for (i = 0; i < priv->num_sensors; i++)
p2[i] = (p2[i] + base2) << shift;
fallthrough; case ONE_PT_CALIB2: case ONE_PT_CALIB2_NO_OFFSET: for (i = 0; i < priv->num_sensors; i++)
p1[i] = (p1[i] + base1) << shift; break; default:
dev_dbg(priv->dev, "calibrationless mode\n"); for (i = 0; i < priv->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
}
/* Apply calibration offset workaround except for _NO_OFFSET modes */ switch (mode) { case TWO_PT_CALIB: for (i = 0; i < priv->num_sensors; i++)
p2[i] += priv->sensor[i].p2_calib_offset;
fallthrough; case ONE_PT_CALIB2: for (i = 0; i < priv->num_sensors; i++)
p1[i] += priv->sensor[i].p1_calib_offset; break;
}
return mode;
}
int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift)
{
u32 p1[MAX_SENSORS], p2[MAX_SENSORS]; int mode;
for (i = 0; i < priv->num_sensors; i++) {
p1[i] = tsens_read_cell(&format->sp[i][0], format->sp_len, cdata0, cdata1);
p2[i] = tsens_read_cell(&format->sp[i][1], format->sp_len, cdata0, cdata1);
}
switch (mode) { case ONE_PT_CALIB: for (i = 0; i < priv->num_sensors; i++)
p1[i] = p1[i] + (base1 << format->base_shift); break; case TWO_PT_CALIB: for (i = 0; i < priv->num_sensors; i++)
p2[i] = (p2[i] + base2) << format->base_shift;
fallthrough; case ONE_PT_CALIB2: for (i = 0; i < priv->num_sensors; i++)
p1[i] = (p1[i] + base1) << format->base_shift; break; default:
dev_dbg(priv->dev, "calibrationless mode\n"); for (i = 0; i < priv->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
}
return mode;
}
/* * Use this function on devices where slope and offset calculations * depend on calibration data read from qfprom. On others the slope * and offset values are derived from tz->tzp->slope and tz->tzp->offset * resp.
*/ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
u32 *p2, u32 mode)
{ int i; int num, den;
for (i = 0; i < priv->num_sensors; i++) {
dev_dbg(priv->dev, "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
__func__, i, p1[i], p2 ? p2[i] : 0);
if (!priv->sensor[i].slope)
priv->sensor[i].slope = SLOPE_DEFAULT; if (mode == TWO_PT_CALIB || mode == TWO_PT_CALIB_NO_OFFSET) { /* * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ * temp_120_degc - temp_30_degc (x2 - x1)
*/
num = p2[i] - p1[i];
num *= SLOPE_FACTOR;
den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
priv->sensor[i].slope = num / den;
}
num = (adc_code * SLOPE_FACTOR) - s->offset;
den = s->slope;
if (num > 0)
degc = num + (den / 2); elseif (num < 0)
degc = num - (den / 2); else
degc = num;
degc /= den;
return degc;
}
/** * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. * @s: Pointer to sensor struct * @field: Index into regmap_field array pointing to temperature data * * 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
*/ staticint tsens_hw_to_mC(conststruct tsens_sensor *s, int field)
{ struct tsens_priv *priv = s->priv;
u32 resolution;
u32 temp = 0; int ret;
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;
/* deciCelsius -> milliCelsius along with sign extension */ return sign_extend32(temp, resolution) * 100;
}
/** * tsens_mC_to_hw - Convert temperature to hardware register value * @s: Pointer to sensor struct * @temp: temperature in milliCelsius to be programmed to hardware * * This function outputs the value to be written to hardware in ADC code * or deciCelsius depending on IP version. * * Return: ADC code or temperature in deciCelsius.
*/ staticint tsens_mC_to_hw(conststruct tsens_sensor *s, int temp)
{ struct tsens_priv *priv = s->priv;
/* milliC to adc code */ if (priv->feat->adc) return degc_to_code(temp / 1000, s);
/* * To enable the interrupt flag for a sensor: * - clear the mask bit * To disable the interrupt flag for a sensor: * - Mask further interrupts for this sensor * - Write 1 followed by 0 to clear the interrupt
*/ switch (irq_type) { case UPPER:
index_mask = UP_INT_MASK_0 + hw_id;
index_clear = UP_INT_CLEAR_0 + hw_id; break; case LOWER:
index_mask = LOW_INT_MASK_0 + hw_id;
index_clear = LOW_INT_CLEAR_0 + hw_id; break; case CRITICAL:
index_mask = CRIT_INT_MASK_0 + hw_id;
index_clear = CRIT_INT_CLEAR_0 + hw_id; break;
}
/** * tsens_set_interrupt - Set state of an interrupt * @priv: Pointer to tsens controller private data * @hw_id: Hardware ID aka. sensor number * @irq_type: irq_type from enum tsens_irq_type * @enable: false = disable, true = enable * * Call IP-specific function to set state of an interrupt * * Return: void
*/ staticvoid tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, enum tsens_irq_type irq_type, bool enable)
{
dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
enable ? "en" : "dis"); if (tsens_version(priv) >= VER_2_X)
tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); else
tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
}
/** * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold * @priv: Pointer to tsens controller private data * @hw_id: Hardware ID aka. sensor number * @d: Pointer to irq state data * * Return: 0 if threshold was not violated, 1 if it was violated and negative * errno in case of errors
*/ staticint tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, struct tsens_irq_data *d)
{ int ret;
ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); if (ret) return ret;
ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); if (ret) return ret;
if (priv->feat->crit_int) {
ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
&d->crit_viol); if (ret) return ret;
}
if (d->up_viol || d->low_viol || d->crit_viol) return 1;
ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); if (ret) return ret;
ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); if (ret) return ret; if (tsens_version(priv) >= VER_2_X) {
ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); if (ret) return ret;
ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); if (ret) return ret;
ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
&d->crit_irq_clear); if (ret) return ret;
ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
&d->crit_irq_mask); if (ret) return ret;
/* v1, v0.1 don't have a irq mask register */ return 0;
}
/** * tsens_critical_irq_thread() - Threaded handler for critical interrupts * @irq: irq number * @data: tsens controller private data * * Check FSM watchdog bark status and clear if needed. * Check all sensors to find ones that violated their critical threshold limits. * Clear and then re-enable the interrupt. * * The level-triggered interrupt might deassert if the temperature returned to * within the threshold limits by the time the handler got scheduled. We * consider the irq to have been handled in that case. * * Return: IRQ_HANDLED
*/ static irqreturn_t tsens_critical_irq_thread(int irq, void *data)
{ struct tsens_priv *priv = data; struct tsens_irq_data d; int temp, ret, i;
u32 wdog_status, wdog_count;
if (priv->feat->has_watchdog) {
ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
&wdog_status); if (ret) return ret;
if (wdog_status) { /* Clear WDOG interrupt */
regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
&wdog_count); if (ret) return ret; if (wdog_count)
dev_dbg(priv->dev, "%s: watchdog count: %d\n",
__func__, wdog_count);
/* Fall through to handle critical interrupts if any */
}
}
for (i = 0; i < priv->num_sensors; i++) { conststruct tsens_sensor *s = &priv->sensor[i];
u32 hw_id = s->hw_id;
if (!s->tzd) continue; if (!tsens_threshold_violated(priv, hw_id, &d)) continue;
ret = get_temp_tsens_valid(s, &temp); if (ret) {
dev_err(priv->dev, "[%u] %s: error reading sensor\n",
hw_id, __func__); continue;
}
tsens_read_irq_state(priv, hw_id, s, &d); if (d.crit_viol &&
!masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { /* Mask critical interrupts, unused on Linux */
tsens_set_interrupt(priv, hw_id, CRITICAL, false);
}
}
return IRQ_HANDLED;
}
/** * tsens_irq_thread - Threaded interrupt handler for uplow interrupts * @irq: irq number * @data: tsens controller private data * * Check all sensors to find ones that violated their threshold limits. If the * temperature is still outside the limits, call thermal_zone_device_update() to * update the thresholds, else re-enable the interrupts. * * The level-triggered interrupt might deassert if the temperature returned to * within the threshold limits by the time the handler got scheduled. We * consider the irq to have been handled in that case. * * Return: IRQ_HANDLED
*/ static irqreturn_t tsens_irq_thread(int irq, void *data)
{ struct tsens_priv *priv = data; struct tsens_irq_data d; int i;
for (i = 0; i < priv->num_sensors; i++) { conststruct tsens_sensor *s = &priv->sensor[i];
u32 hw_id = s->hw_id;
if (!s->tzd) continue; if (!tsens_threshold_violated(priv, hw_id, &d)) continue;
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;
}
/** * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts * @irq: irq number * @data: tsens controller private data * * Handle the combined interrupt as if it were 2 separate interrupts, so call the * critical handler first and then the up/low one. * * Return: IRQ_HANDLED
*/ static irqreturn_t tsens_combined_irq_thread(int irq, void *data)
{
irqreturn_t ret;
ret = tsens_critical_irq_thread(irq, data); if (ret != IRQ_HANDLED) return ret;
int get_temp_tsens_valid(conststruct 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);
return 0;
}
int get_temp_common(conststruct tsens_sensor *s, int *temp)
{ struct tsens_priv *priv = s->priv; int hw_id = s->hw_id; int last_temp = 0, ret, trdy; unsignedlong timeout;
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;
}
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); if (ret) return ret;
*temp = code_to_degc(last_temp, s) * 1000;
return 0;
} while (time_before(jiffies, timeout));
if (op->num_resources > 1) { /* DT with separate SROT and TM address space */
priv->tm_offset = 0;
res = platform_get_resource(op, IORESOURCE_MEM, 1);
srot_base = devm_ioremap_resource(dev, res); if (IS_ERR(srot_base)) {
ret = PTR_ERR(srot_base); goto err_put_device;
}
priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
&tsens_srot_config); if (IS_ERR(priv->srot_map)) {
ret = PTR_ERR(priv->srot_map); goto err_put_device;
}
} else { /* old DTs where SROT and TM were in a contiguous 2K block */
priv->tm_offset = 0x1000;
}
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);
}
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])) {
ret = PTR_ERR(priv->rf[i]); goto err_put_device;
}
}
ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); if (ret) goto err_put_device;
}
priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[TSENS_EN]); if (IS_ERR(priv->rf[TSENS_EN])) {
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; if (!enabled) { switch (tsens_version(priv)) { case VER_1_X_NO_RPM: case VER_2_X_NO_RPM: break; default:
dev_err(dev, "%s: device not enabled\n", __func__);
ret = -ENODEV; goto err_put_device;
}
}
priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[SENSOR_EN]); if (IS_ERR(priv->rf[SENSOR_EN])) {
ret = PTR_ERR(priv->rf[SENSOR_EN]); goto err_put_device;
}
priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[INT_EN]); if (IS_ERR(priv->rf[INT_EN])) {
ret = PTR_ERR(priv->rf[INT_EN]); 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++) { int idx = j + i;
priv->rf[idx] = devm_regmap_field_alloc(dev,
priv->tm_map,
priv->fields[idx]); if (IS_ERR(priv->rf[idx])) {
ret = PTR_ERR(priv->rf[idx]); goto err_put_device;
}
}
}
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++) { int idx = j + i;
priv->rf[idx] =
devm_regmap_field_alloc(dev,
priv->tm_map,
priv->fields[idx]); if (IS_ERR(priv->rf[idx])) {
ret = PTR_ERR(priv->rf[idx]); goto err_put_device;
}
}
}
}
if (tsens_version(priv) >= VER_2_X && ver_minor > 2) { /* Watchdog is present only on v2.3+ */
priv->feat->has_watchdog = 1; for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[i]); if (IS_ERR(priv->rf[i])) {
ret = PTR_ERR(priv->rf[i]); goto err_put_device;
}
} /* * Watchdog is already enabled, unmask the bark. * Disable cycle completion monitoring
*/
regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
regmap_field_write(priv->rf[CC_MON_MASK], 1);
}
spin_lock_init(&priv->ul_lock);
/* VER_0 interrupt doesn't need to be enabled */ if (tsens_version(priv) >= VER_0_1)
tsens_enable_irq(priv);
pdev = of_find_device_by_node(priv->dev->of_node); if (!pdev) return -ENODEV;
irq = platform_get_irq_byname(pdev, irqname); if (irq < 0) {
ret = irq; /* For old DTs with no IRQ defined */ if (irq == -ENXIO)
ret = 0;
} else { /* 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__); else
enable_irq_wake(irq);
}
put_device(&pdev->dev); return ret;
}
#ifdef CONFIG_SUSPEND staticint tsens_reinit(struct tsens_priv *priv)
{ if (tsens_version(priv) >= VER_2_X) { /* * Re-enable the watchdog, unmask the bark. * Disable cycle completion monitoring
*/ if (priv->feat->has_watchdog) {
regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
regmap_field_write(priv->rf[CC_MON_MASK], 1);
}
int tsens_resume_common(struct tsens_priv *priv)
{ if (pm_suspend_target_state == PM_SUSPEND_MEM)
tsens_reinit(priv);
return 0;
}
#endif/* !CONFIG_SUSPEND */
staticint tsens_register(struct tsens_priv *priv)
{ int i, ret; struct thermal_zone_device *tzd;
for (i = 0; i < priv->num_sensors; i++) {
priv->sensor[i].priv = priv;
tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id,
&priv->sensor[i],
&tsens_of_ops); if (IS_ERR(tzd)) continue;
priv->sensor[i].tzd = tzd; if (priv->ops->enable)
priv->ops->enable(priv, i);
devm_thermal_add_hwmon_sysfs(priv->dev, tzd);
}
/* 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));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.