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