1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "aml-tsensor.h"
6 #include "aml-tsensor-regs.h"
7 #include <ddk/debug.h>
8 #include <fbl/auto_call.h>
9 #include <fbl/unique_ptr.h>
10 #include <hw/reg.h>
11 #include <string.h>
12 #include <threads.h>
13 #include <unistd.h>
14 #include <zircon/syscalls/port.h>
15
16 namespace thermal {
17
18 namespace {
19
20 // MMIO indexes.
21 constexpr uint32_t kPllMmio = 0;
22 constexpr uint32_t kAoMmio = 1;
23 constexpr uint32_t kHiuMmio = 2;
24
25 // Thermal calibration magic numbers from uboot.
26 constexpr int32_t kCalA_ = 324;
27 constexpr int32_t kCalB_ = 424;
28 constexpr int32_t kCalC_ = 3159;
29 constexpr int32_t kCalD_ = 9411;
30 constexpr uint32_t kRebootTemp = 130000;
31
32 } // namespace
33
NotifyThermalDaemon()34 zx_status_t AmlTSensor::NotifyThermalDaemon() {
35 zx_port_packet_t thermal_port_packet;
36 thermal_port_packet.key = current_trip_idx_;
37 thermal_port_packet.type = ZX_PKT_TYPE_USER;
38 return zx_port_queue(port_, &thermal_port_packet);
39 }
40
UpdateRiseThresholdIrq(uint32_t irq)41 void AmlTSensor::UpdateRiseThresholdIrq(uint32_t irq) {
42 // Clear the IRQ.
43 auto sensor_ctl = TsCfgReg1::Get().ReadFrom(&*pll_mmio_);
44 auto reg_value = sensor_ctl.reg_value();
45
46 // Disable the IRQ
47 reg_value &= ~(1 << (IRQ_RISE_ENABLE_SHIFT + irq));
48 // Enable corresponding Fall IRQ
49 reg_value |= (1 << (IRQ_FALL_ENABLE_SHIFT + irq));
50 // Clear Rise IRQ Stat.
51 reg_value |= (1 << (IRQ_RISE_STAT_CLR_SHIFT + irq));
52 sensor_ctl.set_reg_value(reg_value);
53 sensor_ctl.WriteTo(&*pll_mmio_);
54
55 // Write 0 to CLR_STAT bit.
56 sensor_ctl = TsCfgReg1::Get().ReadFrom(&*pll_mmio_);
57 reg_value = sensor_ctl.reg_value();
58 reg_value &= ~(1 << (IRQ_RISE_STAT_CLR_SHIFT + irq));
59 sensor_ctl.set_reg_value(reg_value);
60 sensor_ctl.WriteTo(&*pll_mmio_);
61 }
62
UpdateFallThresholdIrq(uint32_t irq)63 void AmlTSensor::UpdateFallThresholdIrq(uint32_t irq) {
64 // Clear the IRQ.
65 auto sensor_ctl = TsCfgReg1::Get().ReadFrom(&*pll_mmio_);
66 auto reg_value = sensor_ctl.reg_value();
67
68 // Disable the IRQ
69 reg_value &= ~(1 << (IRQ_FALL_ENABLE_SHIFT + irq));
70 // Enable corresponding Rise IRQ
71 reg_value |= (1 << (IRQ_RISE_ENABLE_SHIFT + irq));
72 // Clear Fall IRQ Stat.
73 reg_value |= (1 << (IRQ_FALL_STAT_CLR_SHIFT + irq));
74 sensor_ctl.set_reg_value(reg_value);
75 sensor_ctl.WriteTo(&*pll_mmio_);
76
77 // Write 0 to CLR_STAT bit.
78 sensor_ctl = TsCfgReg1::Get().ReadFrom(&*pll_mmio_);
79 reg_value = sensor_ctl.reg_value();
80 reg_value &= ~(1 << (IRQ_FALL_STAT_CLR_SHIFT + irq));
81 sensor_ctl.set_reg_value(reg_value);
82 sensor_ctl.WriteTo(&*pll_mmio_);
83 }
84
TripPointIrqHandler()85 int AmlTSensor::TripPointIrqHandler() {
86 zxlogf(INFO, "%s start\n", __func__);
87 zx_status_t status = ZX_OK;
88
89 // Notify thermal daemon about the default settings.
90 status = NotifyThermalDaemon();
91 if (status != ZX_OK) {
92 zxlogf(ERROR, "aml-tsensor: Failed to send packet via port\n");
93 return status;
94 }
95
96 while (running_.load()) {
97 status = tsensor_irq_.wait(NULL);
98 if (status != ZX_OK) {
99 return status;
100 }
101
102 auto irq_stat = TsStat1::Get().ReadFrom(&*pll_mmio_);
103
104 if (irq_stat.reg_value() & AML_RISE_THRESHOLD_IRQ) {
105 // Handle Rise threshold IRQs.
106 if (irq_stat.rise_th3_irq()) {
107 UpdateRiseThresholdIrq(3);
108 current_trip_idx_ = 4;
109 } else if (irq_stat.rise_th2_irq()) {
110 UpdateRiseThresholdIrq(2);
111 current_trip_idx_ = 3;
112 } else if (irq_stat.rise_th1_irq()) {
113 UpdateRiseThresholdIrq(1);
114 current_trip_idx_ = 2;
115 } else if (irq_stat.rise_th0_irq()) {
116 UpdateRiseThresholdIrq(0);
117 current_trip_idx_ = 1;
118 }
119 } else if (irq_stat.reg_value() & AML_FALL_THRESHOLD_IRQ) {
120 // Handle Fall threshold IRQs.
121 if (irq_stat.fall_th3_irq()) {
122 UpdateFallThresholdIrq(3);
123 current_trip_idx_ = 3;
124 } else if (irq_stat.fall_th2_irq()) {
125 UpdateFallThresholdIrq(2);
126 current_trip_idx_ = 2;
127 } else if (irq_stat.fall_th1_irq()) {
128 UpdateFallThresholdIrq(1);
129 current_trip_idx_ = 1;
130 } else if (irq_stat.fall_th0_irq()) {
131 UpdateFallThresholdIrq(0);
132 current_trip_idx_ = 0;
133 }
134 } else {
135 // Spurious interrupt
136 continue;
137 }
138
139 // Notify thermal daemon about new trip point.
140 status = NotifyThermalDaemon();
141 if (status != ZX_OK) {
142 zxlogf(ERROR, "aml-tsensor: Failed to send packet via port\n");
143 return status;
144 }
145 }
146 return status;
147 }
148
InitTripPoints()149 zx_status_t AmlTSensor::InitTripPoints() {
150 auto set_thresholds = [this](auto&& rise_threshold, auto&& fall_threshold, uint32_t i) {
151 auto rise_temperature_0 = TempToCode(
152 thermal_config_.trip_point_info[i].up_temp,
153 true);
154 auto rise_temperature_1 = TempToCode(
155 thermal_config_.trip_point_info[i + 1].up_temp,
156 true);
157 auto fall_temperature_0 = TempToCode(
158 thermal_config_.trip_point_info[i].down_temp,
159 false);
160 auto fall_temperature_1 = TempToCode(
161 thermal_config_.trip_point_info[i + 1].down_temp,
162 false);
163
164 // Program the 2 rise temperature thresholds.
165 rise_threshold
166 .ReadFrom(&*pll_mmio_)
167 .set_rise_th0(rise_temperature_0)
168 .set_rise_th1(rise_temperature_1)
169 .WriteTo(&*pll_mmio_);
170
171 // Program the 2 fall temperature thresholds.
172 fall_threshold
173 .ReadFrom(&*pll_mmio_)
174 .set_fall_th0(fall_temperature_0)
175 .set_fall_th1(fall_temperature_1)
176 .WriteTo(&*pll_mmio_);
177 };
178
179 // Set rise and fall trip points for the first 4 trip points, since the HW supports only 4.
180 // We skip the 1st entry since it's the default setting for boot up.
181 set_thresholds(TsCfgReg4::Get(), TsCfgReg6::Get(), 1);
182 set_thresholds(TsCfgReg5::Get(), TsCfgReg7::Get(), 3);
183
184 // Clear all IRQ's status.
185 TsCfgReg1::Get()
186 .ReadFrom(&*pll_mmio_)
187 .set_fall_th3_irq_stat_clr(1)
188 .set_fall_th2_irq_stat_clr(1)
189 .set_fall_th1_irq_stat_clr(1)
190 .set_fall_th0_irq_stat_clr(1)
191 .set_rise_th3_irq_stat_clr(1)
192 .set_rise_th2_irq_stat_clr(1)
193 .set_rise_th1_irq_stat_clr(1)
194 .set_rise_th0_irq_stat_clr(1)
195 .WriteTo(&*pll_mmio_);
196
197 TsCfgReg1::Get()
198 .ReadFrom(&*pll_mmio_)
199 .set_fall_th3_irq_stat_clr(0)
200 .set_fall_th2_irq_stat_clr(0)
201 .set_fall_th1_irq_stat_clr(0)
202 .set_fall_th0_irq_stat_clr(0)
203 .set_rise_th3_irq_stat_clr(0)
204 .set_rise_th2_irq_stat_clr(0)
205 .set_rise_th1_irq_stat_clr(0)
206 .set_rise_th0_irq_stat_clr(0)
207 .WriteTo(&*pll_mmio_);
208
209 // Enable all IRQs.
210 TsCfgReg1::Get()
211 .ReadFrom(&*pll_mmio_)
212 .set_rise_th3_irq_en(1)
213 .set_rise_th2_irq_en(1)
214 .set_rise_th1_irq_en(1)
215 .set_rise_th0_irq_en(1)
216 .set_enable_irq(1)
217 .WriteTo(&*pll_mmio_);
218
219 // Start thermal notification thread.
220 auto start_thread = [](void* arg) -> int {
221 return static_cast<AmlTSensor*>(arg)->TripPointIrqHandler();
222 };
223
224 running_.store(true);
225 int rc = thrd_create_with_name(&irq_thread_,
226 start_thread,
227 this,
228 "aml_tsendor_irq_thread");
229 if (rc != thrd_success) {
230 return ZX_ERR_INTERNAL;
231 }
232
233 return ZX_OK;
234 }
235
InitPdev(zx_device_t * parent)236 zx_status_t AmlTSensor::InitPdev(zx_device_t* parent) {
237 zx_status_t status = device_get_protocol(parent,
238 ZX_PROTOCOL_PDEV,
239 &pdev_);
240 if (status != ZX_OK) {
241 return status;
242 }
243
244 // Map amlogic temperature sensopr peripheral control registers.
245 mmio_buffer_t mmio;
246 status = pdev_map_mmio_buffer2(&pdev_, kPllMmio, ZX_CACHE_POLICY_UNCACHED_DEVICE,
247 &mmio);
248 if (status != ZX_OK) {
249 zxlogf(ERROR, "aml-tsensor: could not map periph mmio: %d\n", status);
250 return status;
251 }
252 pll_mmio_ = ddk::MmioBuffer(mmio);
253
254 status = pdev_map_mmio_buffer2(&pdev_, kAoMmio, ZX_CACHE_POLICY_UNCACHED_DEVICE,
255 &mmio);
256 if (status != ZX_OK) {
257 zxlogf(ERROR, "aml-tsensor: could not map periph mmio: %d\n", status);
258 return status;
259 }
260 ao_mmio_ = ddk::MmioBuffer(mmio);
261
262 status = pdev_map_mmio_buffer2(&pdev_, kHiuMmio, ZX_CACHE_POLICY_UNCACHED_DEVICE,
263 &mmio);
264 if (status != ZX_OK) {
265 zxlogf(ERROR, "aml-tsensor: could not map periph mmio: %d\n", status);
266 return status;
267 }
268 hiu_mmio_ = ddk::MmioBuffer(mmio);
269
270 // Map tsensor interrupt.
271 status = pdev_map_interrupt(&pdev_, 0, tsensor_irq_.reset_and_get_address());
272 if (status != ZX_OK) {
273 zxlogf(ERROR, "aml-tsensor: could not map tsensor interrupt\n");
274 return status;
275 }
276
277 return ZX_OK;
278 }
279
280 // Tsensor treats temperature as a mapped temperature code.
281 // The temperature is converted differently depending on the calibration type.
TempToCode(uint32_t temp,bool trend)282 uint32_t AmlTSensor::TempToCode(uint32_t temp, bool trend) {
283 int64_t sensor_code;
284 uint32_t reg_code;
285 uint32_t uefuse = trim_info_ & 0xffff;
286
287 // Referred u-boot code for below magic calculations.
288 // T = 727.8*(u_real+u_efuse/(1<<16)) - 274.7
289 // u_readl = (5.05*YOUT)/((1<<16)+ 4.05*YOUT)
290 // u_readl = (T + 274.7) / 727.8 - u_efuse / (1 << 16)
291 // Yout = (u_readl / (5.05 - 4.05u_readl)) *(1 << 16)
292 if (uefuse & 0x8000) {
293 sensor_code = ((1 << 16) * (temp * 10 + kCalC_) / kCalD_ +
294 (1 << 16) * (uefuse & 0x7fff) / (1 << 16));
295 } else {
296 sensor_code = ((1 << 16) * (temp * 10 + kCalC_) / kCalD_ -
297 (1 << 16) * (uefuse & 0x7fff) / (1 << 16));
298 }
299
300 sensor_code = (sensor_code * 100 / (kCalB_ - kCalA_ * sensor_code / (1 << 16)));
301 if (trend) {
302 reg_code = static_cast<uint32_t>((sensor_code >> 0x4) & AML_TS_TEMP_MASK) + AML_TEMP_CAL;
303 } else {
304 reg_code = ((sensor_code >> 0x4) & AML_TS_TEMP_MASK);
305 }
306 return reg_code;
307 }
308
309 // Calculate a temperature value from a temperature code.
310 // The unit of the temperature is degree Celsius.
CodeToTemp(uint32_t temp_code)311 uint32_t AmlTSensor::CodeToTemp(uint32_t temp_code) {
312 uint32_t sensor_temp = temp_code;
313 uint32_t uefuse = trim_info_ & 0xffff;
314
315 // Referred u-boot code for below magic calculations.
316 // T = 727.8*(u_real+u_efuse/(1<<16)) - 274.7
317 // u_readl = (5.05*YOUT)/((1<<16)+ 4.05*YOUT)
318 sensor_temp = ((sensor_temp * kCalB_) / 100 * (1 << 16) /
319 (1 * (1 << 16) + kCalA_ * sensor_temp / 100));
320 if (uefuse & 0x8000) {
321 sensor_temp = (1000 * ((sensor_temp - (uefuse & (0x7fff))) * kCalD_ / (1 << 16) - kCalC_) / 10);
322 } else {
323 sensor_temp = 1000 * ((sensor_temp + uefuse) * kCalD_ / (1 << 16) - kCalC_) / 10;
324 }
325 return sensor_temp;
326 }
327
ReadTemperature()328 uint32_t AmlTSensor::ReadTemperature() {
329 int count = 0;
330 unsigned int value_all = 0;
331
332 // Datasheet is incorrect.
333 // Referred to u-boot code.
334 // Yay magic numbers.
335 for (int j = 0; j < AML_TS_VALUE_CONT; j++) {
336 auto ts_stat0 = TsStat0::Get().ReadFrom(&*pll_mmio_);
337 auto tvalue = ts_stat0.temperature();
338
339 if ((tvalue >= 0x18a9) && (tvalue <= 0x32a6)) {
340 count++;
341 value_all += tvalue;
342 }
343 }
344 if (count == 0) {
345 return 0;
346 } else {
347 return CodeToTemp(value_all / count) / MCELSIUS;
348 }
349 }
350
SetRebootTemperature(uint32_t temp)351 void AmlTSensor::SetRebootTemperature(uint32_t temp) {
352 uint32_t reboot_val = TempToCode(kRebootTemp / MCELSIUS, true);
353 auto reboot_config = TsCfgReg2::Get().ReadFrom(&*pll_mmio_);
354
355 reboot_config.set_hi_temp_enable(1)
356 .set_reset_en(1)
357 .set_high_temp_times(AML_TS_REBOOT_TIME)
358 .set_high_temp_threshold(reboot_val << 4)
359 .WriteTo(&*pll_mmio_);
360 }
361
GetStateChangePort(zx_handle_t * port)362 zx_status_t AmlTSensor::GetStateChangePort(zx_handle_t* port) {
363 return zx_handle_duplicate(port_, ZX_RIGHT_SAME_RIGHTS, port);
364 }
365
InitSensor(zx_device_t * parent,thermal_device_info_t thermal_config)366 zx_status_t AmlTSensor::InitSensor(zx_device_t* parent, thermal_device_info_t thermal_config) {
367 zx_status_t status = InitPdev(parent);
368 if (status != ZX_OK) {
369 return status;
370 }
371
372 // Copy the thermal_config
373 memcpy(&thermal_config_, &thermal_config, sizeof(thermal_device_info_t));
374
375 // Get the trim info.
376 trim_info_ = ao_mmio_->Read32(AML_TRIM_INFO);
377
378 // Set the clk.
379 hiu_mmio_->Write32(AML_HHI_TS_CLK_ENABLE, AML_HHI_TS_CLK_CNTL);
380
381 // Not setting IRQ's here.
382 auto sensor_ctl = TsCfgReg1::Get().ReadFrom(&*pll_mmio_);
383 sensor_ctl.set_filter_en(1)
384 .set_ts_ana_en_vcm(1)
385 .set_ts_ana_en_vbg(1)
386 .set_bipolar_bias_current_input(AML_TS_CH_SEL)
387 .set_ts_ena_en_iptat(1)
388 .set_ts_dem_en(1)
389 .WriteTo(&*pll_mmio_);
390
391 // Create a port to send messages to thermal daemon.
392 status = zx_port_create(0, &port_);
393 if (status != ZX_OK) {
394 zxlogf(ERROR, "aml-tsensor: Unable to create port\n");
395 return status;
396 }
397
398 // Setup IRQ's and rise/fall thresholds.
399 return InitTripPoints();
400 }
401
~AmlTSensor()402 AmlTSensor::~AmlTSensor() {
403 running_.store(false);
404 thrd_join(irq_thread_, NULL);
405 tsensor_irq_.destroy();
406 }
407
408 } // namespace thermal
409