blob: e1abd2db22602ecc952f4272d8d0485822972be1 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/hardware/adc/llcpp/fidl.h>
#include <fuchsia/hardware/temperature/llcpp/fidl.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <cmath>
#include <mock-mmio-reg/mock-mmio-reg.h>
#include <soc/aml-common/aml-g12-saradc.h>
#include <soc/aml-s905d2/s905d2-hw.h>
#include <zxtest/zxtest.h>
#include "../thermistor.h"
namespace {
bool FloatNear(float a, float b) { return std::abs(a - b) < 0.1f; }
} // namespace
namespace thermal {
using TemperatureClient = ::llcpp::fuchsia::hardware::temperature::Device::SyncClient;
using AdcClient = ::llcpp::fuchsia::hardware::adc::Device::SyncClient;
NtcInfo ntc_info[] = {
{.part = "ncpXXwf104",
.profile =
{
{.temperature_c = -40, .resistance_ohm = 4397119}, // 0
{.temperature_c = -35, .resistance_ohm = 3088599},
{.temperature_c = -30, .resistance_ohm = 2197225},
{.temperature_c = -25, .resistance_ohm = 1581881},
{.temperature_c = -20, .resistance_ohm = 1151037},
{.temperature_c = -15, .resistance_ohm = 846579},
{.temperature_c = -10, .resistance_ohm = 628988},
{.temperature_c = -5, .resistance_ohm = 471632},
{.temperature_c = 0, .resistance_ohm = 357012},
{.temperature_c = 5, .resistance_ohm = 272500},
{.temperature_c = 10, .resistance_ohm = 209710}, // 10
{.temperature_c = 15, .resistance_ohm = 162651},
{.temperature_c = 20, .resistance_ohm = 127080},
{.temperature_c = 25, .resistance_ohm = 100000},
{.temperature_c = 30, .resistance_ohm = 79222},
{.temperature_c = 35, .resistance_ohm = 63167},
{.temperature_c = 40, .resistance_ohm = 50677},
{.temperature_c = 45, .resistance_ohm = 40904},
{.temperature_c = 50, .resistance_ohm = 33195},
{.temperature_c = 55, .resistance_ohm = 27091},
{.temperature_c = 60, .resistance_ohm = 22224}, // 20
{.temperature_c = 65, .resistance_ohm = 18323},
{.temperature_c = 70, .resistance_ohm = 15184},
{.temperature_c = 75, .resistance_ohm = 12635},
{.temperature_c = 80, .resistance_ohm = 10566},
{.temperature_c = 85, .resistance_ohm = 8873},
{.temperature_c = 90, .resistance_ohm = 7481},
{.temperature_c = 95, .resistance_ohm = 6337},
{.temperature_c = 100, .resistance_ohm = 5384},
{.temperature_c = 105, .resistance_ohm = 4594},
{.temperature_c = 110, .resistance_ohm = 3934}, // 30
{.temperature_c = 115, .resistance_ohm = 3380},
{.temperature_c = 120, .resistance_ohm = 2916},
{.temperature_c = 125, .resistance_ohm = 2522}, // 33
}},
};
class TestSarAdc : public AmlSaradcDevice {
public:
static constexpr uint32_t kMaxChannels = 4;
TestSarAdc(ddk::MmioBuffer adc_mmio, ddk::MmioBuffer ao_mmio, zx::interrupt irq)
: AmlSaradcDevice(std::move(adc_mmio), std::move(ao_mmio), std::move(irq)) {}
void HwInit() override {}
void Shutdown() override {}
zx_status_t GetSample(uint32_t channel, uint32_t* outval) override {
if (channel >= kMaxChannels) {
return ZX_ERR_INVALID_ARGS;
}
*outval = values_[channel];
return ZX_OK;
}
void SetReadValue(uint32_t ch, uint32_t value) { values_[ch] = value; }
private:
uint32_t values_[kMaxChannels];
};
class ThermistorDeviceTest : public zxtest::Test {
public:
ThermistorDeviceTest() {}
uint32_t CalcSampleValue(NtcInfo info, uint32_t idx, uint32_t pullup) {
uint32_t ntc_resistance = info.profile[idx].resistance_ohm;
float ratio = static_cast<float>(ntc_resistance) / static_cast<float>(ntc_resistance + pullup);
float sample = roundf(ratio * static_cast<float>((1 << adc_->Resolution()) - 1));
return static_cast<uint32_t>(sample);
}
void SetUp() override {
constexpr size_t kRegSize = S905D2_SARADC_LENGTH / sizeof(uint32_t); // in 32 bits chunks.
fbl::Array<ddk_mock::MockMmioReg> regs0 =
fbl::Array(new ddk_mock::MockMmioReg[kRegSize], kRegSize);
fbl::Array<ddk_mock::MockMmioReg> regs1 =
fbl::Array(new ddk_mock::MockMmioReg[kRegSize], kRegSize);
ddk_mock::MockMmioRegRegion mock0(regs0.data(), sizeof(uint32_t), kRegSize);
ddk_mock::MockMmioRegRegion mock1(regs1.data(), sizeof(uint32_t), kRegSize);
zx::interrupt irq;
adc_ = fbl::MakeRefCounted<TestSarAdc>(mock0.GetMmioBuffer(), mock1.GetMmioBuffer(),
std::move(irq));
thermistor_ = std::make_unique<ThermistorChannel>(fake_ddk::kFakeParent, adc_, 0, ntc_info[0],
kPullupValue);
const auto message_op = [](void* ctx, fidl_incoming_msg_t* msg,
fidl_txn_t* txn) -> zx_status_t {
return static_cast<ThermistorChannel*>(ctx)->DdkMessage(msg, txn);
};
ASSERT_OK(messenger_.SetMessageOp(thermistor_.get(), message_op));
}
protected:
static constexpr uint32_t kPullupValue = 47000;
std::unique_ptr<ThermistorChannel> thermistor_;
fbl::RefPtr<TestSarAdc> adc_;
fake_ddk::FidlMessenger messenger_;
};
TEST_F(ThermistorDeviceTest, GetTemperatureCelsius) {
TemperatureClient client(std::move(messenger_.local()));
{
uint32_t ntc_idx = 10;
adc_->SetReadValue(0, CalcSampleValue(ntc_info[0], ntc_idx, kPullupValue));
auto result = client.GetTemperatureCelsius();
EXPECT_OK(result->status);
EXPECT_TRUE(FloatNear(result->temp, ntc_info[0].profile[ntc_idx].temperature_c));
}
{ // set read value to 0, which should be out of range of the ntc table
adc_->SetReadValue(0, 0);
auto result = client.GetTemperatureCelsius();
EXPECT_NOT_OK(result->status);
}
{ // set read value to max, which should be out of range of ntc table
adc_->SetReadValue(0, (1 << adc_->Resolution()) - 1);
auto result = client.GetTemperatureCelsius();
EXPECT_NOT_OK(result->status);
}
}
} // namespace thermal