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 "mtk-thermal.h"
6 
7 #include <ddk/protocol/platform/device.h>
8 #include <ddktl/pdev.h>
9 #include <fbl/unique_ptr.h>
10 #include <soc/mt8167/mt8167-hw.h>
11 
12 #include "mtk-thermal-reg.h"
13 
14 namespace {
15 
16 constexpr uint32_t kTsCon1Addr = 0x10018604;
17 constexpr uint32_t kAuxAdcCon1SetAddr = 0x11003008;
18 constexpr uint32_t kAuxAdcCon1ClrAddr = 0x1100300c;
19 constexpr uint32_t kAuxAdcDat11Addr = 0x11003040;
20 constexpr uint32_t kAuxAdcChannel = 11;
21 constexpr uint32_t kAuxAdcBits = 12;
22 
23 constexpr int kSensorCount = 3;
24 
25 constexpr uint32_t kKelvinOffset = 2732;  // Units: 0.1 degrees C
26 
27 // TODO(bradenkell): Figure out what the actual time base is (66 MHz or 32 kHz?) and calculate
28 //                   these instead of hard coding.
29 constexpr uint32_t kThermalPeriod = 12;
30 constexpr uint32_t kSenseInterval = 429;
31 constexpr uint32_t kAhbPollPeriod = 768;
32 
FixedPoint(int32_t value)33 int32_t FixedPoint(int32_t value) {
34     return (value * 10000) >> 12;
35 }
36 
RawWithGain(int32_t raw,int32_t gain)37 int32_t RawWithGain(int32_t raw, int32_t gain) {
38     return (FixedPoint(raw) * 10000) / gain;
39 }
40 
41 }  // namespace
42 
43 namespace thermal {
44 
Create(zx_device_t * parent)45 zx_status_t MtkThermal::Create(zx_device_t* parent) {
46     zx_status_t status;
47 
48     ddk::PDev pdev(parent);
49     if (!pdev.is_valid()) {
50         zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available\n", __FILE__);
51         return ZX_ERR_NO_RESOURCES;
52     }
53 
54     ddk::ClkProtocolClient clk(parent);
55     if (!clk.is_valid()) {
56         zxlogf(ERROR, "%s: ZX_PROTOCOL_CLK not available\n", __FILE__);
57         return ZX_ERR_NO_RESOURCES;
58     }
59 
60     pdev_device_info_t info;
61     if ((status = pdev.GetDeviceInfo(&info)) != ZX_OK) {
62         zxlogf(ERROR, "%s: pdev_get_device_info failed\n", __FILE__);
63         return status;
64     }
65 
66     std::optional<ddk::MmioBuffer> mmio;
67     if ((status = pdev.MapMmio(0, &mmio)) != ZX_OK) {
68         zxlogf(ERROR, "%s: MapMmio failed\n", __FILE__);
69         return status;
70     }
71 
72     std::optional<ddk::MmioBuffer> fuse_mmio;
73     if ((status = pdev.MapMmio(1, &fuse_mmio)) != ZX_OK) {
74         zxlogf(ERROR, "%s: MapMmio failed\n", __FILE__);
75         return status;
76     }
77 
78     std::optional<ddk::MmioBuffer> pll_mmio;
79     if ((status = pdev.MapMmio(2, &pll_mmio)) != ZX_OK) {
80         zxlogf(ERROR, "%s: MapMmio failed\n", __FILE__);
81         return status;
82     }
83 
84     std::optional<ddk::MmioBuffer> pmic_mmio;
85     if ((status = pdev.MapMmio(3, &pmic_mmio)) != ZX_OK) {
86         zxlogf(ERROR, "%s: MapMmio failed\n", __FILE__);
87         return status;
88     }
89 
90     thermal_device_info_t thermal_info;
91     size_t actual;
92     status = device_get_metadata(parent, THERMAL_CONFIG_METADATA, &thermal_info,
93                                  sizeof(thermal_info), &actual);
94     if (status != ZX_OK || actual != sizeof(thermal_info)) {
95         zxlogf(ERROR, "%s: device_get_metadata failed\n", __FILE__);
96         return status == ZX_OK ? ZX_ERR_INTERNAL : status;
97     }
98 
99     fbl::AllocChecker ac;
100     fbl::unique_ptr<MtkThermal> device(
101         new (&ac) MtkThermal(parent, std::move(*mmio), std::move(*fuse_mmio), std::move(*pll_mmio),
102                              std::move(*pmic_mmio), clk, info, thermal_info));
103     if (!ac.check()) {
104         zxlogf(ERROR, "%s: MtkThermal alloc failed\n", __FILE__);
105         return ZX_ERR_NO_MEMORY;
106     }
107 
108     if ((status = device->Init()) != ZX_OK) {
109         return status;
110     }
111 
112     if ((status = device->DdkAdd("mtk-thermal")) != ZX_OK) {
113         zxlogf(ERROR, "%s: DdkAdd failed\n", __FILE__);
114         return status;
115     }
116 
117     __UNUSED auto* dummy = device.release();
118 
119     return ZX_OK;
120 }
121 
Init()122 zx_status_t MtkThermal::Init() {
123     for (uint32_t i = 0; i < clk_count_; i++) {
124         zx_status_t status = clk_.Enable(i);
125         if (status != ZX_OK) {
126             zxlogf(ERROR, "%s: Failed to enable clock %u\n", __FILE__, i);
127             return status;
128         }
129     }
130 
131     // Set the initial DVFS operating point. The bootloader sets it to 1.001 GHz @ 1.2 V.
132     constexpr dvfs_info_t dvfs_info = {
133         .op_idx = 0,
134         .power_domain = BIG_CLUSTER_POWER_DOMAIN
135     };
136 
137     zx_status_t status = SetDvfsOpp(&dvfs_info);
138     if (status != ZX_OK) {
139         return status;
140     }
141 
142     TempMonCtl0::Get().ReadFrom(&mmio_).disable_all().WriteTo(&mmio_);
143 
144     TempMsrCtl0::Get()
145         .ReadFrom(&mmio_)
146         .set_msrctl0(TempMsrCtl0::kSample1)
147         .set_msrctl1(TempMsrCtl0::kSample1)
148         .set_msrctl2(TempMsrCtl0::kSample1)
149         .set_msrctl3(TempMsrCtl0::kSample1)
150         .WriteTo(&mmio_);
151 
152     TempAhbTimeout::Get().FromValue(0xffffffff).WriteTo(&mmio_);
153     TempAdcPnp::Get(0).FromValue(0).WriteTo(&mmio_);
154     TempAdcPnp::Get(1).FromValue(1).WriteTo(&mmio_);
155     TempAdcPnp::Get(2).FromValue(2).WriteTo(&mmio_);
156 
157     // Set the thermal controller to read from the spare registers, then wait for the dummy sensor
158     // reading to end up in TempMsr0-2.
159     TempMonCtl1::Get().ReadFrom(&mmio_).set_period(1).WriteTo(&mmio_);
160     TempMonCtl2::Get().ReadFrom(&mmio_).set_sen_interval(1).WriteTo(&mmio_);
161     TempAhbPoll::Get().FromValue(1).WriteTo(&mmio_);
162 
163     constexpr uint32_t dummy_temp = (1 << kAuxAdcBits) - 1;
164     TempSpare::Get(0).FromValue(dummy_temp | (1 << kAuxAdcBits)).WriteTo(&mmio_);
165 
166     TempPnpMuxAddr::Get().FromValue(TempSpare::Get(2).addr() + MT8167_THERMAL_BASE).WriteTo(&mmio_);
167     TempAdcMuxAddr::Get().FromValue(TempSpare::Get(2).addr() + MT8167_THERMAL_BASE).WriteTo(&mmio_);
168     TempAdcEnAddr::Get().FromValue(TempSpare::Get(1).addr() + MT8167_THERMAL_BASE).WriteTo(&mmio_);
169     TempAdcValidAddr::Get()
170         .FromValue(TempSpare::Get(0).addr() + MT8167_THERMAL_BASE)
171         .WriteTo(&mmio_);
172     TempAdcVoltAddr::Get()
173         .FromValue(TempSpare::Get(0).addr() + MT8167_THERMAL_BASE)
174         .WriteTo(&mmio_);
175 
176     TempRdCtrl::Get().ReadFrom(&mmio_).set_diff(TempRdCtrl::kValidVoltageSame).WriteTo(&mmio_);
177     TempAdcValidMask::Get()
178         .ReadFrom(&mmio_)
179         .set_polarity(TempAdcValidMask::kActiveHigh)
180         .set_pos(kAuxAdcBits)
181         .WriteTo(&mmio_);
182     TempAdcVoltageShift::Get().FromValue(0).WriteTo(&mmio_);
183     TempMonCtl0::Get().ReadFrom(&mmio_).enable_all().WriteTo(&mmio_);
184 
185     for (int i = 0; i < kSensorCount; i++) {
186         auto msr = TempMsr::Get(i).ReadFrom(&mmio_);
187         for (; msr.valid() == 0 || msr.reading() != dummy_temp; msr.ReadFrom(&mmio_)) {}
188     }
189 
190     TempMonCtl0::Get().ReadFrom(&mmio_).disable_all().WriteTo(&mmio_);
191 
192     // Set the thermal controller to get temperature readings from the aux ADC.
193     TempMonCtl1::Get().ReadFrom(&mmio_).set_period(kThermalPeriod).WriteTo(&mmio_);
194     TempMonCtl2::Get()
195         .ReadFrom(&mmio_)
196         .set_sen_interval(kSenseInterval)
197         .set_filt_interval(1)
198         .WriteTo(&mmio_);
199     TempAhbPoll::Get().FromValue(kAhbPollPeriod).WriteTo(&mmio_);
200 
201     TempAdcEn::Get().FromValue(1 << kAuxAdcChannel).WriteTo(&mmio_);
202     TempAdcMux::Get().FromValue(1 << kAuxAdcChannel).WriteTo(&mmio_);
203 
204     TempPnpMuxAddr::Get().FromValue(kTsCon1Addr).WriteTo(&mmio_);
205     TempAdcEnAddr::Get().FromValue(kAuxAdcCon1SetAddr).WriteTo(&mmio_);
206     TempAdcMuxAddr::Get().FromValue(kAuxAdcCon1ClrAddr).WriteTo(&mmio_);
207     TempAdcValidAddr::Get().FromValue(kAuxAdcDat11Addr).WriteTo(&mmio_);
208     TempAdcVoltAddr::Get().FromValue(kAuxAdcDat11Addr).WriteTo(&mmio_);
209 
210     TempAdcWriteCtrl::Get()
211         .ReadFrom(&mmio_)
212         .set_mux_write_en(1)
213         .set_pnp_write_en(1)
214         .WriteTo(&mmio_);
215 
216     TempMonCtl0::Get().ReadFrom(&mmio_).enable_real().WriteTo(&mmio_);
217 
218     return ZX_OK;
219 }
220 
PmicRead(uint32_t addr)221 uint16_t MtkThermal::PmicRead(uint32_t addr) {
222     while (PmicReadData::Get().ReadFrom(&pmic_mmio_).status() != PmicReadData::kStateIdle) {}
223 
224     PmicCmd::Get().FromValue(0).set_write(0).set_addr(addr).WriteTo(&pmic_mmio_);
225 
226     auto pmic_read = PmicReadData::Get().FromValue(0);
227     while (pmic_read.ReadFrom(&pmic_mmio_).status() != PmicReadData::kStateValid) {}
228 
229     uint16_t ret = static_cast<uint16_t>(pmic_read.data());
230 
231     PmicValidClear::Get().ReadFrom(&pmic_mmio_).set_valid_clear(1).WriteTo(&pmic_mmio_);
232 
233     return ret;
234 }
235 
PmicWrite(uint16_t data,uint32_t addr)236 void MtkThermal::PmicWrite(uint16_t data, uint32_t addr) {
237     while (PmicReadData::Get().ReadFrom(&pmic_mmio_).status() != PmicReadData::kStateIdle) {}
238     PmicCmd::Get().FromValue(0).set_write(1).set_addr(addr).set_data(data).WriteTo(&pmic_mmio_);
239 }
240 
RawToTemperature(uint32_t raw,int sensor)241 uint32_t MtkThermal::RawToTemperature(uint32_t raw, int sensor) {
242     auto cal0 = TempCalibration0::Get().ReadFrom(&fuse_mmio_);
243     auto cal1 = TempCalibration1::Get().ReadFrom(&fuse_mmio_);
244     auto cal2 = TempCalibration2::Get().ReadFrom(&fuse_mmio_);
245 
246     int32_t vts = cal2.get_vts3();
247     if (sensor == 0) {
248         vts = cal0.get_vts0();
249     } else if (sensor == 1) {
250         vts = cal0.get_vts1();
251     } else if (sensor == 2) {
252         vts = cal2.get_vts2();
253     }
254 
255     // See misc/mediatek/thermal/mt8167/mtk_ts_cpu.c in the Linux kernel source.
256     int32_t gain = 10000 + FixedPoint(cal1.get_adc_gain());
257     int32_t vts_with_gain = RawWithGain(vts - cal1.get_adc_offset(), gain);
258     int32_t temp_c = ((RawWithGain(raw - cal1.get_adc_offset(), gain) - vts_with_gain) * 5) / 6;
259     int32_t slope = cal0.slope_sign() == 0 ? cal0.slope() : -cal0.slope();
260     temp_c = cal0.temp_offset() - ((temp_c * 100) / (165 + (cal1.id() == 0 ? 0 : slope)));
261     return temp_c + kKelvinOffset;
262 }
263 
GetTemperature(uint32_t * temp)264 zx_status_t MtkThermal::GetTemperature(uint32_t* temp) {
265     *temp = 0;
266     for (int i = 0; i < kSensorCount; i++) {
267         auto msr = TempMsr::Get(i).ReadFrom(&mmio_);
268         if (!msr.valid()) {
269             continue;
270         }
271 
272         uint32_t sensor_temp = RawToTemperature(msr.reading(), i);
273         if (sensor_temp > *temp) {
274             *temp = sensor_temp;
275         }
276     }
277 
278     return ZX_OK;
279 }
280 
SetDvfsOpp(const dvfs_info_t * opp)281 zx_status_t MtkThermal::SetDvfsOpp(const dvfs_info_t* opp) {
282     if (opp->power_domain >= MAX_DVFS_DOMAINS) {
283         return ZX_ERR_INVALID_ARGS;
284     }
285 
286     const scpi_opp_t& opps = thermal_info_.opps[opp->power_domain];
287     if (opp->op_idx >= opps.count) {
288         return ZX_ERR_OUT_OF_RANGE;
289     }
290 
291     uint32_t new_freq = opps.opp[opp->op_idx].freq_hz;
292     uint32_t new_volt = opps.opp[opp->op_idx].volt_mv;
293 
294     if (new_volt > VprocCon10::kMaxVoltageUv || new_volt < VprocCon10::kMinVoltageUv) {
295         return ZX_ERR_OUT_OF_RANGE;
296     }
297 
298     auto armpll = ArmPllCon1::Get().ReadFrom(&pll_mmio_);
299     uint32_t old_freq = armpll.frequency();
300 
301     auto vproc = VprocCon10::Get().FromValue(0).set_voltage(new_volt);
302     if (vproc.voltage() != new_volt) {
303         // The requested voltage is not a multiple of the voltage step.
304         return ZX_ERR_INVALID_ARGS;
305     }
306 
307     if (new_freq > old_freq) {
308         PmicWrite(vproc.reg_value(), vproc.reg_addr());
309         armpll.set_frequency(new_freq).WriteTo(&pll_mmio_);
310     } else {
311         armpll.set_frequency(new_freq).WriteTo(&pll_mmio_);
312         PmicWrite(vproc.reg_value(), vproc.reg_addr());
313     }
314 
315     current_opp_idx_ = opp->op_idx;
316 
317     return ZX_OK;
318 }
319 
DdkIoctl(uint32_t op,const void * in_buf,size_t in_len,void * out_buf,size_t out_len,size_t * actual)320 zx_status_t MtkThermal::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
321                                  size_t out_len, size_t* actual) {
322     switch (op) {
323     case IOCTL_THERMAL_GET_TEMPERATURE:
324         if (out_len != sizeof(uint32_t)) {
325             return ZX_ERR_INVALID_ARGS;
326         }
327 
328         *actual = sizeof(uint32_t);
329         return GetTemperature(reinterpret_cast<uint32_t*>(out_buf));
330 
331     case IOCTL_THERMAL_GET_DEVICE_INFO:
332         if (out_len != sizeof(thermal_info_)) {
333             return ZX_ERR_INVALID_ARGS;
334         }
335 
336         memcpy(out_buf, &thermal_info_, sizeof(thermal_info_));
337         *actual = sizeof(thermal_info_);
338         return ZX_OK;
339 
340     case IOCTL_THERMAL_SET_DVFS_OPP:
341         if (in_len != sizeof(dvfs_info_t)) {
342             return ZX_ERR_INVALID_ARGS;
343         }
344 
345         return SetDvfsOpp(reinterpret_cast<const dvfs_info_t*>(in_buf));
346 
347     case IOCTL_THERMAL_GET_DVFS_INFO: {
348         if (in_len != sizeof(uint32_t) || out_len != sizeof(thermal_info_.opps[0])) {
349             return ZX_ERR_INVALID_ARGS;
350         }
351 
352         uint32_t domain = *reinterpret_cast<const uint32_t*>(in_buf);
353         if (domain >= MAX_DVFS_DOMAINS) {
354             return ZX_ERR_INVALID_ARGS;
355         }
356 
357         memcpy(out_buf, &thermal_info_.opps[domain], sizeof(thermal_info_.opps[0]));
358         *actual = sizeof(thermal_info_.opps[0]);
359         return ZX_OK;
360     }
361 
362     case IOCTL_THERMAL_GET_DVFS_OPP: {
363         if (in_len != sizeof(uint32_t) || out_len != sizeof(uint32_t)) {
364             return ZX_ERR_INVALID_ARGS;
365         }
366 
367         uint32_t domain = *reinterpret_cast<const uint32_t*>(in_buf);
368         if (domain != BIG_CLUSTER_POWER_DOMAIN) {
369             return ZX_ERR_INVALID_ARGS;
370         }
371 
372         uint32_t* opp_idx = reinterpret_cast<uint32_t*>(out_buf);
373 
374         *opp_idx = current_opp_idx_;
375         *actual = sizeof(*opp_idx);
376         return ZX_OK;
377     }
378 
379     // TODO(bradenkell): Implement the rest of these.
380     case IOCTL_THERMAL_GET_INFO:
381     case IOCTL_THERMAL_SET_TRIP:
382     case IOCTL_THERMAL_GET_STATE_CHANGE_EVENT:
383     case IOCTL_THERMAL_GET_STATE_CHANGE_PORT:
384     case IOCTL_THERMAL_SET_FAN_LEVEL:
385     case IOCTL_THERMAL_GET_FAN_LEVEL:
386     default:
387         break;
388     }
389 
390     return ZX_ERR_NOT_SUPPORTED;
391 }
392 
393 }  // namespace thermal
394 
mtk_thermal_bind(void * ctx,zx_device_t * parent)395 extern "C" zx_status_t mtk_thermal_bind(void* ctx, zx_device_t* parent) {
396     return thermal::MtkThermal::Create(parent);
397 }
398