blob: c735562d4e052aa9b3e71220e63af188dd535fd2 [file] [log] [blame]
// Copyright 2019 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 "aml-pwm.h"
#include <vector>
#include <ddk/metadata/pwm.h>
#include <fbl/alloc_checker.h>
#include <fbl/array.h>
#include <mock-mmio-reg/mock-mmio-reg.h>
#include <soc/aml-common/aml-pwm-regs.h>
namespace {
constexpr size_t kRegSize = 0x00001000 / sizeof(uint32_t); // in 32 bits chunks.
} // namespace
namespace pwm {
class FakeAmlPwmDevice : public AmlPwmDevice {
public:
static std::unique_ptr<FakeAmlPwmDevice> Create(fdf::MmioBuffer mmio0, fdf::MmioBuffer mmio1,
fdf::MmioBuffer mmio2, fdf::MmioBuffer mmio3,
fdf::MmioBuffer mmio4,
std::vector<pwm_id_t> ids) {
fbl::AllocChecker ac;
auto device = fbl::make_unique_checked<FakeAmlPwmDevice>(&ac);
if (!ac.check()) {
zxlogf(ERROR, "%s: device object alloc failed", __func__);
return nullptr;
}
std::vector<fdf::MmioBuffer> mmios;
mmios.push_back(std::move(mmio0));
mmios.push_back(std::move(mmio1));
mmios.push_back(std::move(mmio2));
mmios.push_back(std::move(mmio3));
mmios.push_back(std::move(mmio4));
device->Init(std::move(mmios), ids);
return device;
}
explicit FakeAmlPwmDevice() : AmlPwmDevice() {}
};
class AmlPwmDeviceTest : public zxtest::Test {
public:
void SetUp() override {
fbl::AllocChecker ac;
mock_mmio0_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac,
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio0_ alloc failed", __func__);
return;
}
mock_mmio1_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac,
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio1_ alloc failed", __func__);
return;
}
mock_mmio2_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac,
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio2_ alloc failed", __func__);
return;
}
mock_mmio3_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac,
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio3_ alloc failed", __func__);
return;
}
mock_mmio4_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac,
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio4_ alloc failed", __func__);
return;
}
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFEFFFFF5); // SetMode
(*mock_mmio1_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
(*mock_mmio2_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
(*mock_mmio2_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFEFFFFF5); // SetMode
(*mock_mmio3_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
(*mock_mmio3_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFEFFFFF5); // SetMode
(*mock_mmio4_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
(*mock_mmio4_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFEFFFFF5); // SetMode
fdf::MmioBuffer mmio0(mock_mmio0_->GetMmioBuffer());
fdf::MmioBuffer mmio1(mock_mmio1_->GetMmioBuffer());
fdf::MmioBuffer mmio2(mock_mmio2_->GetMmioBuffer());
fdf::MmioBuffer mmio3(mock_mmio3_->GetMmioBuffer());
fdf::MmioBuffer mmio4(mock_mmio4_->GetMmioBuffer());
// Protect channel 3 for protect tests
std::vector<pwm_id_t> ids = {{.id = 0}, {.id = 1}, {.id = 2}, {.id = 3, .init = false},
{.id = 4}, {.id = 5}, {.id = 6}, {.id = 7},
{.id = 8}, {.id = 9}};
pwm_ = FakeAmlPwmDevice::Create(std::move(mmio0), std::move(mmio1), std::move(mmio2),
std::move(mmio3), std::move(mmio4), ids);
ASSERT_NOT_NULL(pwm_);
}
void TearDown() override {
mock_mmio0_->VerifyAll();
mock_mmio1_->VerifyAll();
mock_mmio2_->VerifyAll();
mock_mmio3_->VerifyAll();
mock_mmio4_->VerifyAll();
}
protected:
std::unique_ptr<FakeAmlPwmDevice> pwm_;
// Mmio Regs and Regions
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio0_;
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio1_;
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio2_;
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio3_;
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio4_;
};
TEST_F(AmlPwmDeviceTest, ProtectTest) {
mode_config mode_cfg{
.mode = static_cast<Mode>(100),
.regular = {},
};
pwm_config cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&mode_cfg),
.mode_config_size = sizeof(mode_cfg),
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(3, &cfg));
}
TEST_F(AmlPwmDeviceTest, GetConfigTest) {
mode_config mode_cfg{
.mode = static_cast<Mode>(100),
.regular = {},
};
pwm_config cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&mode_cfg),
.mode_config_size = sizeof(mode_cfg),
};
EXPECT_OK(pwm_->PwmImplGetConfig(0, &cfg));
cfg.mode_config_buffer = nullptr;
EXPECT_NOT_OK(pwm_->PwmImplGetConfig(0, &cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidNullConfig) {
// config is null
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, nullptr));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidNoModeBuffer) {
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
// config has no mode buffer
.mode_config_buffer = nullptr,
.mode_config_size = 0,
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidModeConfigSizeIncorrect) {
mode_config fail_mode{
.mode = Mode::kOn,
.regular = {},
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
// mode_config_size incorrect
.mode_config_size = 10,
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidTwoTimerTimer2InvalidDutyCycle) {
mode_config fail_mode{
.mode = Mode::kTwoTimer,
.two_timer = {},
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
.mode_config_size = sizeof(fail_mode),
};
// Invalid duty cycle for timer 2.
fail_mode.two_timer.duty_cycle2 = -10.0;
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
fail_mode.two_timer.duty_cycle2 = 120.0;
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidTimer1InvalidDutyCycle) {
mode_config fail_mode{
.mode = Mode::kOn,
.regular = {},
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
.mode_config_size = sizeof(fail_mode),
};
// Invalid duty cycle for timer 1.
fail_cfg.duty_cycle = -10.0;
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
fail_cfg.duty_cycle = 120.0;
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidTimer1InvalidMode) {
mode_config fail_mode{
// Invalid mode
.mode = static_cast<Mode>(100),
.regular = {},
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
.mode_config_size = sizeof(fail_mode),
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidPwmId) {
for (Mode mode : {Mode::kOn, Mode::kOff, Mode::kTwoTimer, Mode::kDeltaSigma}) {
mode_config fail{
.mode = mode,
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail),
.mode_config_size = sizeof(fail),
};
// Incorrect pwm ID.
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(10, &fail_cfg));
}
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidTimer1PeriodExceedsLimit) {
mode_config fail_mode{
.mode = aml_pwm::Mode::kOn,
.regular = {},
};
pwm_config fail_cfg{
.polarity = false,
// period = 1 second, exceeds the maximum allowed period (343'927'680 ns).
.period_ns = 1'000'000'000,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
.mode_config_size = sizeof(fail_mode),
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigInvalidTwoTimerModeTimer2PeriodExceedsLimit) {
mode_config fail_mode{
.mode = aml_pwm::Mode::kTwoTimer,
.two_timer =
{
// period = 1 second, exceeds the maximum allowed period (343'927'680 ns).
.period_ns2 = 1'000'000'000,
},
};
pwm_config fail_cfg{
.polarity = false,
.period_ns = 1000,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&fail_mode),
.mode_config_size = sizeof(fail_mode),
};
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &fail_cfg));
}
TEST_F(AmlPwmDeviceTest, SetConfigTest) {
// Mode::kOff
mode_config off{
.mode = Mode::kOff,
.regular = {},
};
pwm_config off_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&off),
.mode_config_size = sizeof(off),
};
EXPECT_OK(pwm_->PwmImplSetConfig(0, &off_cfg));
(*mock_mmio0_)[2 * 4].ExpectRead(0x01000000).ExpectWrite(0x01000001); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF80FF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFBFFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x001E0000); // SetDutyCycle
mode_config on{
.mode = Mode::kOn,
.regular = {},
};
pwm_config on_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&on),
.mode_config_size = sizeof(on),
};
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // turn on
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFDFFFFFA); // SetMode
EXPECT_OK(pwm_->PwmImplSetConfig(0, &off_cfg));
EXPECT_OK(pwm_->PwmImplSetConfig(0, &off_cfg)); // same configs
// Mode::kOn
(*mock_mmio0_)[2 * 4].ExpectRead(0x01000000).ExpectWrite(0x00000002); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFF80FFFF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xF7FFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x20000000); // EnableConst
(*mock_mmio0_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x001E0000); // SetDutyCycle
EXPECT_OK(pwm_->PwmImplSetConfig(1, &on_cfg));
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x08000000); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xDFFFFFFF); // EnableConst
(*mock_mmio0_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00060010); // SetDutyCycle
on_cfg.polarity = true;
on_cfg.period_ns = 1000;
on_cfg.duty_cycle = 30.0;
EXPECT_OK(pwm_->PwmImplSetConfig(1, &on_cfg)); // Change Duty Cycle
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFEFFFFF5); // SetMode
EXPECT_OK(pwm_->PwmImplSetConfig(1, &off_cfg)); // Change Mode
// Mode::kDeltaSigma
(*mock_mmio1_)[2 * 4].ExpectRead(0x02000000).ExpectWrite(0x00000004); // SetMode
(*mock_mmio1_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF80FF); // SetClockDivider
(*mock_mmio1_)[3 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF0064); // SetDSSetting
(*mock_mmio1_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFBFFFFFF); // Invert
(*mock_mmio1_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xEFFFFFFF); // EnableConst
(*mock_mmio1_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00060010); // SetDutyCycle
mode_config ds{
.mode = Mode::kDeltaSigma,
.delta_sigma =
{
.delta = 100,
},
};
pwm_config ds_cfg{
.polarity = false,
.period_ns = 1000,
.duty_cycle = 30.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&ds),
.mode_config_size = sizeof(ds),
};
EXPECT_OK(pwm_->PwmImplSetConfig(2, &ds_cfg));
// Mode::kTwoTimer
(*mock_mmio3_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x01000002); // SetMode
(*mock_mmio3_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFF80FFFF); // SetClockDivider
(*mock_mmio3_)[6 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00130003); // SetDutyCycle2
(*mock_mmio3_)[4 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF0302); // SetTimers
(*mock_mmio3_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xF7FFFFFF); // Invert
(*mock_mmio3_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xDFFFFFFF); // EnableConst
(*mock_mmio3_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00060010); // SetDutyCycle
mode_config timer2{
.mode = Mode::kTwoTimer,
.two_timer =
{
.period_ns2 = 1000,
.duty_cycle2 = 80.0,
.timer1 = 3,
.timer2 = 2,
},
};
pwm_config timer2_cfg{
.polarity = false,
.period_ns = 1000,
.duty_cycle = 30.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&timer2),
.mode_config_size = sizeof(timer2),
};
EXPECT_OK(pwm_->PwmImplSetConfig(7, &timer2_cfg));
}
TEST_F(AmlPwmDeviceTest, SingleTimerModeClockDividerChange) {
(*mock_mmio0_)[2 * 4].ExpectRead(0x01000000).ExpectWrite(0x01000001); // SetMode
// Expected clock divider value = 1, raw value of divider register field = 0
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF80FF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFBFFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x001E0000); // SetDutyCycle
mode_config on{
.mode = Mode::kOn,
.regular = {},
};
pwm_config on_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&on),
.mode_config_size = sizeof(on),
};
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Success
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x5F460000); // SetDutyCycle
on_cfg.period_ns = 1'000'000; // Doesn't trigger the divider change.
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Success
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF81FF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x8EE90000); // SetDutyCycle
on_cfg.period_ns = 3'000'000;
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Success
on_cfg.period_ns = 1'000'000'000; // 1 Hz, exceeds the maximum period
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Failure
}
TEST_F(AmlPwmDeviceTest, TwoTimerModeClockDividerChange) {
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x01000002); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFF80FFFF); // SetClockDivider
(*mock_mmio0_)[6 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00130003); // SetDutyCycle2
(*mock_mmio0_)[4 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF0302); // SetTimers
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xF7FFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xDFFFFFFF); // EnableConst
(*mock_mmio0_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00060010); // SetDutyCycle
mode_config timer2{
.mode = Mode::kTwoTimer,
.two_timer =
{
.period_ns2 = 1000,
.duty_cycle2 = 80.0,
.timer1 = 3,
.timer2 = 2,
},
};
pwm_config timer2_cfg{
.polarity = false,
.period_ns = 1000,
.duty_cycle = 30.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&timer2),
.mode_config_size = sizeof(timer2),
};
EXPECT_OK(pwm_->PwmImplSetConfig(1, &timer2_cfg));
// timer1 needs divider = 2, timer2 needs divider = 1,
// so the divider = max(2, 1) = 2. The raw value is set to (2 - 1) = 1.
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFF81FFFF); // SetClockDivider
(*mock_mmio0_)[6 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x00090001); // SetDutyCycle2
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xDFFFFFFF); // EnableConst
(*mock_mmio0_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x2ADF6408); // SetDutyCycle
timer2_cfg.period_ns = 3'000'000;
EXPECT_OK(pwm_->PwmImplSetConfig(1, &timer2_cfg)); // Success
// timer1 needs divider = 2, timer2 needs divider = 3,
// so the divider = max(2, 3) = 3. The raw value is set to (3 - 1) = 2.
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFF82FFFF); // SetClockDivider
(*mock_mmio0_)[6 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x986F261B); // SetDutyCycle2
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xDFFFFFFF); // EnableConst
(*mock_mmio0_)[1 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x1C9442B0); // SetDutyCycle
timer2.two_timer.period_ns2 = 6'000'000;
EXPECT_OK(pwm_->PwmImplSetConfig(1, &timer2_cfg)); // Success
}
TEST_F(AmlPwmDeviceTest, SetConfigFailTest) {
(*mock_mmio0_)[2 * 4].ExpectRead(0x01000000).ExpectWrite(0x01000001); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF80FF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFBFFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x001E0000); // SetDutyCycle
mode_config on{
.mode = Mode::kOn,
.regular = {},
};
pwm_config on_cfg{
.polarity = false,
.period_ns = 1250,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&on),
.mode_config_size = sizeof(on),
};
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Success
// Nothing should happen on the register if the input is incorrect.
(*mock_mmio0_)[2 * 4].VerifyAndClear();
(*mock_mmio0_)[0 * 4].VerifyAndClear();
on_cfg.polarity = true;
on_cfg.duty_cycle = 120.0;
EXPECT_NOT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Fail
}
TEST_F(AmlPwmDeviceTest, EnableTest) {
EXPECT_NOT_OK(pwm_->PwmImplEnable(10)); // Fail
(*mock_mmio1_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x00008000);
EXPECT_OK(pwm_->PwmImplEnable(2));
EXPECT_OK(pwm_->PwmImplEnable(2)); // Enable twice
(*mock_mmio2_)[2 * 4].ExpectRead(0x00008000).ExpectWrite(0x00808000);
EXPECT_OK(pwm_->PwmImplEnable(5)); // Enable other PWMs
}
TEST_F(AmlPwmDeviceTest, DisableTest) {
EXPECT_NOT_OK(pwm_->PwmImplDisable(10)); // Fail
EXPECT_OK(pwm_->PwmImplDisable(0)); // Disable first
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x00008000);
EXPECT_OK(pwm_->PwmImplEnable(0));
(*mock_mmio0_)[2 * 4].ExpectRead(0x00008000).ExpectWrite(0x00000000);
EXPECT_OK(pwm_->PwmImplDisable(0));
EXPECT_OK(pwm_->PwmImplDisable(0)); // Disable twice
(*mock_mmio2_)[2 * 4].ExpectRead(0x00008000).ExpectWrite(0x00808000);
EXPECT_OK(pwm_->PwmImplEnable(5)); // Enable other PWMs
(*mock_mmio2_)[2 * 4].ExpectRead(0x00808000).ExpectWrite(0x00008000);
EXPECT_OK(pwm_->PwmImplDisable(5)); // Disable other PWMs
}
TEST_F(AmlPwmDeviceTest, SetConfigPeriodNotDivisibleBy100Test) {
(*mock_mmio0_)[2 * 4].ExpectRead(0x01000000).ExpectWrite(0x01000001); // SetMode
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFF80FF); // SetClockDivider
(*mock_mmio0_)[2 * 4].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFBFFFFFF); // Invert
(*mock_mmio0_)[2 * 4].ExpectRead(0x00000000).ExpectWrite(0x10000000); // EnableConst
(*mock_mmio0_)[0 * 4].ExpectRead(0xA39D9259).ExpectWrite(0x10420000); // SetDutyCycle
mode_config on{
.mode = Mode::kOn,
.regular = {},
};
pwm_config on_cfg{
.polarity = false,
.period_ns = 170625,
.duty_cycle = 100.0,
.mode_config_buffer = reinterpret_cast<uint8_t*>(&on),
.mode_config_size = sizeof(on),
};
EXPECT_OK(pwm_->PwmImplSetConfig(0, &on_cfg)); // Success
}
} // namespace pwm