/** * struct exynos_tmu_data : A structure to hold the private data of the TMU * driver * @base: base address of the single instance of the TMU controller. * @base_second: base address of the common registers of the TMU controller. * @irq: irq number of the TMU controller. * @soc: id of the SOC type. * @lock: lock to implement synchronization. * @clk: pointer to the clock structure. * @clk_sec: pointer to the clock structure for accessing the base_second. * @sclk: pointer to the clock structure for accessing the tmu special clk. * @cal_type: calibration type for temperature * @efuse_value: SoC defined fuse value * @min_efuse_value: minimum valid trimming data * @max_efuse_value: maximum valid trimming data * @temp_error1: fused value of the first point trim. * @temp_error2: fused value of the second point trim. * @gain: gain of amplifier in the positive-TC generator block * 0 < gain <= 15 * @reference_voltage: reference voltage of amplifier * in the positive-TC generator block * 0 < reference_voltage <= 31 * @tzd: pointer to thermal_zone_device structure * @enabled: current status of TMU device * @tmu_set_low_temp: SoC specific method to set trip (falling threshold) * @tmu_set_high_temp: SoC specific method to set trip (rising threshold) * @tmu_set_crit_temp: SoC specific method to set critical temperature * @tmu_disable_low: SoC specific method to disable an interrupt (falling threshold) * @tmu_disable_high: SoC specific method to disable an interrupt (rising threshold) * @tmu_initialize: SoC specific TMU initialization method * @tmu_control: SoC specific TMU control method * @tmu_read: SoC specific TMU temperature read method * @tmu_set_emulation: SoC specific TMU emulation setting method * @tmu_clear_irqs: SoC specific TMU interrupts clearing method
*/ struct exynos_tmu_data { void __iomem *base; void __iomem *base_second; int irq; enum soc_type soc; struct mutex lock; struct clk *clk, *clk_sec, *sclk;
u32 cal_type;
u32 efuse_value;
u32 min_efuse_value;
u32 max_efuse_value;
u16 temp_error1, temp_error2;
u8 gain;
u8 reference_voltage; struct thermal_zone_device *tzd; bool enabled;
/* * TMU treats temperature as a mapped temperature code. * The temperature is converted differently depending on the calibration type.
*/ staticint temp_to_code(struct exynos_tmu_data *data, u8 temp)
{ if (data->cal_type == TYPE_ONE_POINT_TRIMMING) return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM;
/* * Calculate a temperature value from a temperature code. * The unit of the temperature is degree Celsius.
*/ staticint code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
{ if (data->cal_type == TYPE_ONE_POINT_TRIMMING) return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM;
staticvoid exynos4210_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
{ /* * Failing thresholds are not supported on Exynos 4210. * We use polling instead.
*/
}
staticvoid exynos4210_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{ /* * Hardware critical temperature handling is not supported on Exynos 4210. * We still set the critical temperature threshold, but this is only to * make sure it is handled as soon as possible. It is just a normal interrupt.
*/
/* On exynos5420 the triminfo register is in the shared space */ if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); else
trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
staticvoid exynos7_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{ /* * Like Exynos 4210, Exynos 7 does not seem to support critical temperature * handling in hardware. Again, we still set a separate interrupt for it.
*/
exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_RISE7_6 + 0, 16, temp);
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true);
}
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) {
con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
con |= BIT(EXYNOS7_PD_DET_EN_SHIFT);
} else {
con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT);
con &= ~BIT(EXYNOS7_PD_DET_EN_SHIFT);
}
staticint exynos_get_temp(struct thermal_zone_device *tz, int *temp)
{ struct exynos_tmu_data *data = thermal_zone_device_priv(tz); int value, ret = 0;
if (!data || !data->tmu_read) return -EINVAL; elseif (!data->enabled) /* * Called too early, probably * from thermal_zone_of_sensor_register().
*/ return -EAGAIN;
mutex_lock(&data->lock);
clk_enable(data->clk);
value = data->tmu_read(data); if (value < 0)
ret = value; else
*temp = code_to_temp(data, value) * MCELSIUS;
val_irq = readl(data->base + tmu_intstat); /* * Clear the interrupts. Please note that the documentation for * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly * states that INTCLEAR register has a different placing of bits * responsible for FALL IRQs than INTSTAT register. Exynos5420 * and Exynos5440 documentation is correct (Exynos4210 doesn't * support FALL IRQs at all).
*/
writel(val_irq, data->base + tmu_intclear);
}
/* * Check if the TMU shares some registers and then try to map the * memory of common registers.
*/ if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) return 0;
if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
dev_err(&pdev->dev, "failed to get Resource 1\n"); return -ENODEV;
}
data->base_second = devm_ioremap(&pdev->dev, res.start,
resource_size(&res)); if (!data->base_second) {
dev_err(&pdev->dev, "Failed to ioremap memory\n"); return -ENOMEM;
}
return 0;
}
staticint exynos_set_trips(struct thermal_zone_device *tz, int low, int high)
{ struct exynos_tmu_data *data = thermal_zone_device_priv(tz);
mutex_lock(&data->lock);
clk_enable(data->clk);
if (low > INT_MIN)
data->tmu_set_low_temp(data, low / MCELSIUS); else
data->tmu_disable_low(data); if (high < INT_MAX)
data->tmu_set_high_temp(data, high / MCELSIUS); else
data->tmu_disable_high(data);
/* * Try enabling the regulator if found * TODO: Add regulator as an SOC feature, so that regulator enable * is a compulsory call.
*/
ret = devm_regulator_get_enable_optional(dev, "vtmu"); switch (ret) { case 0: case -ENODEV: break; case -EPROBE_DEFER: return -EPROBE_DEFER; default:
dev_err(dev, "Failed to get enabled regulator: %d\n", ret); return ret;
}
ret = exynos_map_dt_data(pdev); if (ret) return ret;
data->clk = devm_clk_get(dev, "tmu_apbif"); if (IS_ERR(data->clk)) return dev_err_probe(dev, PTR_ERR(data->clk), "Failed to get clock\n");
data->clk_sec = devm_clk_get(dev, "tmu_triminfo_apbif"); if (IS_ERR(data->clk_sec)) { if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) return dev_err_probe(dev, PTR_ERR(data->clk_sec), "Failed to get triminfo clock\n");
} else {
ret = clk_prepare(data->clk_sec); if (ret) {
dev_err(dev, "Failed to get clock\n"); return ret;
}
}
ret = clk_prepare(data->clk); if (ret) {
dev_err(dev, "Failed to get clock\n"); goto err_clk_sec;
}
switch (data->soc) { case SOC_ARCH_EXYNOS5433: case SOC_ARCH_EXYNOS7:
data->sclk = devm_clk_get(dev, "tmu_sclk"); if (IS_ERR(data->sclk)) {
ret = dev_err_probe(dev, PTR_ERR(data->sclk), "Failed to get sclk\n"); goto err_clk;
} else {
ret = clk_prepare_enable(data->sclk); if (ret) {
dev_err(dev, "Failed to enable sclk\n"); goto err_clk;
}
} break; default: break;
}
ret = exynos_tmu_initialize(pdev); if (ret) {
dev_err(dev, "Failed to initialize TMU\n"); goto err_sclk;
}
data->tzd = devm_thermal_of_zone_register(dev, 0, data,
&exynos_sensor_ops); if (IS_ERR(data->tzd)) {
ret = dev_err_probe(dev, PTR_ERR(data->tzd), "Failed to register sensor\n"); goto err_sclk;
}
ret = exynos_thermal_zone_configure(pdev); if (ret) {
dev_err(dev, "Failed to configure the thermal zone\n"); goto err_sclk;
}
ret = devm_request_threaded_irq(dev, data->irq, NULL,
exynos_tmu_threaded_irq,
IRQF_TRIGGER_RISING
| IRQF_SHARED | IRQF_ONESHOT,
dev_name(dev), data); if (ret) {
dev_err(dev, "Failed to request irq: %d\n", data->irq); goto err_sclk;
}
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.