// SPDX-License-Identifier: GPL-2.0 /* * Thermal sensor driver for Allwinner SOC * Copyright (C) 2019 Yangtao Li * * Based on the work of Icenowy Zheng <icenowy@aosc.io> * Based on the work of Ondrej Jirman <megous@megous.com> * Based on the work of Josef Gajdusek <atx@atx.name>
*/
struct ths_thermal_chip { bool has_mod_clk; bool has_bus_clk_reset; bool needs_sram; int sensor_num; int offset; int scale; int ft_deviation; int temp_data_base; int (*calibrate)(struct ths_device *tmdev,
u16 *caldata, int callen); int (*init)(struct ths_device *tmdev); unsignedlong (*irq_ack)(struct ths_device *tmdev); int (*calc_temp)(struct ths_device *tmdev, int id, int reg);
};
/* The H616 needs to have a bit 16 in the SRAM control register cleared. */ staticconststruct reg_field sun8i_ths_sram_reg_field = REG_FIELD(0x0, 16, 16);
/* ths have no data yet */ if (!val) return -EAGAIN;
*temp = tmdev->chip->calc_temp(tmdev, s->id, val); /* * According to the original sdk, there are some platforms(rarely) * that add a fixed offset value after calculating the temperature * value. We can't simply put it on the formula for calculating the * temperature above, because the formula for calculating the * temperature above is also used when the sensor is calibrated. If * do this, the correct calibration formula is hard to know.
*/
*temp += tmdev->chip->ft_deviation;
staticunsignedlong sun8i_h3_irq_ack(struct ths_device *tmdev)
{ unsignedlong irq_bitmap = 0; int i, state;
regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
for (i = 0; i < tmdev->chip->sensor_num; i++) { if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
regmap_write(tmdev->regmap, SUN8I_THS_IS,
SUN8I_THS_DATA_IRQ_STS(i));
bitmap_set(&irq_bitmap, i, 1);
}
}
return irq_bitmap;
}
staticunsignedlong sun50i_h6_irq_ack(struct ths_device *tmdev)
{ unsignedlong irq_bitmap = 0; int i, state;
for (i = 0; i < tmdev->chip->sensor_num; i++) { if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
SUN50I_H6_THS_DATA_IRQ_STS(i));
bitmap_set(&irq_bitmap, i, 1);
}
}
for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) { /* We allow some zones to not register. */ if (IS_ERR(tmdev->sensor[i].tzd)) continue;
thermal_zone_device_update(tmdev->sensor[i].tzd,
THERMAL_EVENT_UNSPECIFIED);
}
return IRQ_HANDLED;
}
staticint sun8i_h3_ths_calibrate(struct ths_device *tmdev,
u16 *caldata, int callen)
{ int i;
if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num) return -EINVAL;
for (i = 0; i < tmdev->chip->sensor_num; i++) { int offset = (i % 2) << 4;
staticint sun50i_h6_ths_calibrate(struct ths_device *tmdev,
u16 *caldata, int callen)
{ struct device *dev = tmdev->dev; int i, ft_temp;
if (!caldata[0]) return -EINVAL;
/* * efuse layout: * * 0 11 16 27 32 43 48 57 * +----------+-----------+-----------+-----------+ * | temp | |sensor0| |sensor1| |sensor2| | * +----------+-----------+-----------+-----------+ * ^ ^ ^ * | | | * | | sensor3[11:8] * | sensor3[7:4] * sensor3[3:0] * * The calibration data on the H6 is the ambient temperature and * sensor values that are filled during the factory test stage. * * The unit of stored FT temperature is 0.1 degree celsius. * * We need to calculate a delta between measured and caluclated * register values and this will become a calibration offset.
*/
ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
for (i = 0; i < tmdev->chip->sensor_num; i++) { int sensor_reg, sensor_temp, cdata, offset;
sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
/* * Calibration data is CALIBRATE_DEFAULT - (calculated * temperature from sensor reading at factory temperature * minus actual factory temperature) * 14.88 (scale from * temperature to register values)
*/
cdata = CALIBRATE_DEFAULT -
((sensor_temp - ft_temp) * 10 / tmdev->chip->scale); if (cdata & ~TEMP_CALIB_MASK) { /* * Calibration value more than 12-bit, but calibration * register is 12-bit. In this case, ths hardware can * still work without calibration, although the data * won't be so accurate.
*/
dev_warn(dev, "sensor%d is not calibrated.\n", i); continue;
}
calcell = nvmem_cell_get(dev, "calibration"); if (IS_ERR(calcell)) { if (PTR_ERR(calcell) == -EPROBE_DEFER) return -EPROBE_DEFER; /* * Even if the external calibration data stored in sid is * not accessible, the THS hardware can still work, although * the data won't be so accurate. * * The default value of calibration register is 0x800 for * every sensor, and the calibration value is usually 0x7xx * or 0x8xx, so they won't be away from the default value * for a lot. * * So here we do not return error if the calibration data is * not available, except the probe needs deferring.
*/ goto out;
}
caldata = nvmem_cell_read(calcell, &callen); if (IS_ERR(caldata)) {
ret = PTR_ERR(caldata); goto out;
}
tmdev->chip->calibrate(tmdev, caldata, callen);
kfree(caldata);
out: if (!IS_ERR(calcell))
nvmem_cell_put(calcell); return ret;
}
sram_pdev = of_find_device_by_node(sram_node); if (!sram_pdev) { /* platform device might not be probed yet */ return ERR_PTR(-EPROBE_DEFER);
}
/* If no regmap is found then the other device driver is at fault */
regmap = dev_get_regmap(&sram_pdev->dev, NULL); if (!regmap)
regmap = ERR_PTR(-EINVAL);
staticint sun50i_h6_thermal_init(struct ths_device *tmdev)
{ int val;
/* The H616 needs to have a bit in the SRAM control register cleared. */ if (tmdev->sram_regmap_field)
regmap_field_write(tmdev->sram_regmap_field, 0);
/* * The manual recommends an overall sample frequency of 50 KHz (20us, * 480 cycles at 24 MHz), which provides plenty of time for both the * acquisition time (>24 cycles) and the actual conversion time * (>14 cycles). * The lower half of the CTRL register holds the "acquire time", in * clock cycles, which the manual recommends to be 2us: * 24MHz * 2us = 48 cycles. * The high half of THS_CTRL encodes the sample frequency, in clock * cycles: 24MHz * 20us = 480 cycles. * This is explained in the H616 manual, but apparently wrongly * described in the H6 manual, although the BSP code does the same * for both SoCs.
*/
regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
SUN50I_THS_CTRL0_T_ACQ(48) |
SUN50I_THS_CTRL0_T_SAMPLE_PER(480)); /* average over 4 samples */
regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
SUN50I_THS_FILTER_EN |
SUN50I_THS_FILTER_TYPE(1)); /* * clkin = 24MHz * filter_samples = 4 * period = 0.25s * * x = period * clkin / 4096 / filter_samples - 1 * = 365
*/
regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
SUN50I_H6_THS_PC_TEMP_PERIOD(365)); /* enable sensor */
val = GENMASK(tmdev->chip->sensor_num - 1, 0);
regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val); /* thermal data interrupt enable */
val = GENMASK(tmdev->chip->sensor_num - 1, 0);
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
return 0;
}
staticint sun8i_ths_register(struct ths_device *tmdev)
{ int i;
for (i = 0; i < tmdev->chip->sensor_num; i++) {
tmdev->sensor[i].tmdev = tmdev;
tmdev->sensor[i].id = i;
tmdev->sensor[i].tzd =
devm_thermal_of_zone_register(tmdev->dev,
i,
&tmdev->sensor[i],
&ths_ops);
/* * If an individual zone fails to register for reasons * other than probe deferral (eg, a bad DT) then carry * on, other zones might register successfully.
*/ if (IS_ERR(tmdev->sensor[i].tzd)) { if (PTR_ERR(tmdev->sensor[i].tzd) == -EPROBE_DEFER) return PTR_ERR(tmdev->sensor[i].tzd); continue;
}
tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL); if (!tmdev) return -ENOMEM;
tmdev->dev = dev;
tmdev->chip = of_device_get_match_data(&pdev->dev); if (!tmdev->chip) return -EINVAL;
ret = sun8i_ths_resource_init(tmdev); if (ret) return ret;
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ret = tmdev->chip->init(tmdev); if (ret) return ret;
ret = sun8i_ths_register(tmdev); if (ret) return ret;
/* * Avoid entering the interrupt handler, the thermal device is not * registered yet, we deffer the registration of the interrupt to * the end.
*/
ret = devm_request_threaded_irq(dev, irq, NULL,
sun8i_irq_thread,
IRQF_ONESHOT, "ths", tmdev); if (ret) return ret;
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.