[as370][clks] Add CPU clocking
Test: syn-clk-test unit tests and performance changes on subsequent
CLs.
Bug: 33796
Change-Id: Id1c515b3f0774e7a8e7a19693f36ef6ae9849a01
diff --git a/zircon/system/dev/board/as370/as370-clock.cc b/zircon/system/dev/board/as370/as370-clock.cc
index 852d805..b4026ca 100644
--- a/zircon/system/dev/board/as370/as370-clock.cc
+++ b/zircon/system/dev/board/as370/as370-clock.cc
@@ -26,10 +26,15 @@
.base = as370::kAudioGlobalBase,
.length = as370::kAudioGlobalSize,
},
+ {
+ .base = as370::kCpuBase,
+ .length = as370::kCpuSize,
+ },
};
static const clock_id_t clock_ids[] = {
{as370::As370Clk::kClkAvpll0},
{as370::As370Clk::kClkAvpll1},
+ {as370::As370Clk::kClkCpu},
};
static const pbus_metadata_t clock_metadata[] = {
{
diff --git a/zircon/system/dev/clk/syn-clk/syn-clk-test.cc b/zircon/system/dev/clk/syn-clk/syn-clk-test.cc
index c6c7813..beadfaf 100644
--- a/zircon/system/dev/clk/syn-clk/syn-clk-test.cc
+++ b/zircon/system/dev/clk/syn-clk/syn-clk-test.cc
@@ -12,9 +12,11 @@
class SynClkTest : public SynClk {
public:
- SynClkTest(ddk_mock::MockMmioRegRegion& global_mmio, ddk_mock::MockMmioRegRegion& audio_mmio)
+ SynClkTest(ddk_mock::MockMmioRegRegion& global_mmio, ddk_mock::MockMmioRegRegion& audio_mmio,
+ ddk_mock::MockMmioRegRegion& cpu_mmio)
: SynClk(nullptr, ddk::MmioBuffer(global_mmio.GetMmioBuffer()),
- ddk::MmioBuffer(audio_mmio.GetMmioBuffer())) {}
+ ddk::MmioBuffer(audio_mmio.GetMmioBuffer()),
+ ddk::MmioBuffer(cpu_mmio.GetMmioBuffer())) {}
};
TEST(ClkSynTest, AvpllClkEnable) {
@@ -22,7 +24,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
global_regs[0x0530 / 4].ExpectRead(0x00000000).ExpectWrite(0x00000001); // Enable AVIO clock.
global_regs[0x0088 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffffe); // Not sysPll power down.
@@ -40,7 +43,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffffb); // Disable AVPLL.
audio_regs[0x0000 / 4].ExpectRead(0xffffffff).ExpectWrite(0xffffffdf); // Disable AVPLL Clock.
@@ -56,7 +60,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffff7); // Disable AVPLL 1.
audio_regs[0x0020 / 4].ExpectRead(0xffffffff).ExpectWrite(0xffffffdf); // Disable AVPLL Clock.
@@ -72,7 +77,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
EXPECT_NOT_OK(test.ClockImplSetRate(0, 800'000'001)); // Too high.
}
@@ -82,7 +88,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffffb); // Clock disable.
audio_regs[0x0018 / 4].ExpectRead(0x00000000).ExpectWrite(0x00000001); // Bypass.
@@ -105,7 +112,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffffb); // Clock disable.
audio_regs[0x0018 / 4].ExpectRead(0x00000000).ExpectWrite(0x00000001); // Bypass.
@@ -131,7 +139,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffffb); // Clock disable.
audio_regs[0x0018 / 4].ExpectRead(0x00000000).ExpectWrite(0x00000001); // Bypass.
@@ -157,7 +166,8 @@
auto audio_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kAudioGlobalSize / 4);
ddk_mock::MockMmioRegRegion global_region(global_regs.get(), 4, as370::kGlobalSize / 4);
ddk_mock::MockMmioRegRegion audio_region(audio_regs.get(), 4, as370::kAudioGlobalSize / 4);
- SynClkTest test(global_region, audio_region);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(global_region, audio_region, unused);
audio_regs[0x0044 / 4].ExpectRead(0xffffffff).ExpectWrite(0xfffffff7); // Clock disable.
audio_regs[0x0038 / 4].ExpectRead(0x00000000).ExpectWrite(0x00000001); // Bypass.
@@ -175,4 +185,34 @@
audio_region.VerifyAll();
}
+TEST(ClkSynTest, CpuPllSetRate1800) {
+ auto cpu_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kCpuSize / 4);
+ ddk_mock::MockMmioRegRegion cpu_region(cpu_regs.get(), 4, as370::kCpuSize / 4);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(unused, unused, cpu_region);
+
+ cpu_regs[0x2000 / 4].ExpectWrite(0x00404806);
+ cpu_regs[0x2004 / 4].ExpectWrite(0x00000000);
+ cpu_regs[0x200c / 4].ExpectWrite(0x22000000);
+
+ EXPECT_OK(test.ClockImplSetRate(2, 1'800'000'000));
+
+ cpu_region.VerifyAll();
+}
+
+TEST(ClkSynTest, CpuPllSetRate1000) {
+ auto cpu_regs = std::make_unique<ddk_mock::MockMmioReg[]>(as370::kCpuSize / 4);
+ ddk_mock::MockMmioRegRegion cpu_region(cpu_regs.get(), 4, as370::kCpuSize / 4);
+ ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint32_t), as370::kCpuSize / 4);
+ SynClkTest test(unused, unused, cpu_region);
+
+ cpu_regs[0x2000 / 4].ExpectWrite(0x00402806);
+ cpu_regs[0x2004 / 4].ExpectWrite(0x00000000);
+ cpu_regs[0x200c / 4].ExpectWrite(0x22000000);
+
+ EXPECT_OK(test.ClockImplSetRate(2, 1'000'000'000));
+
+ cpu_region.VerifyAll();
+}
+
} // namespace clk
diff --git a/zircon/system/dev/clk/syn-clk/syn-clk.cc b/zircon/system/dev/clk/syn-clk/syn-clk.cc
index 67e5f2e..25df873 100644
--- a/zircon/system/dev/clk/syn-clk/syn-clk.cc
+++ b/zircon/system/dev/clk/syn-clk/syn-clk.cc
@@ -4,6 +4,8 @@
#include "syn-clk.h"
+#include <lib/device-protocol/pdev.h>
+
#include <numeric>
#include <ddk/binding.h>
@@ -15,7 +17,6 @@
#include <fbl/auto_lock.h>
#include <fbl/unique_ptr.h>
#include <hwreg/bitfields.h>
-#include <lib/device-protocol/pdev.h>
#include <soc/as370/as370-audio-regs.h>
#include <soc/as370/as370-clk-regs.h>
#include <soc/as370/as370-clk.h>
@@ -29,7 +30,7 @@
return ZX_ERR_NO_RESOURCES;
}
- std::optional<ddk::MmioBuffer> global_mmio, avio_mmio;
+ std::optional<ddk::MmioBuffer> global_mmio, avio_mmio, cpu_mmio;
auto status = pdev.MapMmio(0, &global_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to map mmio index 0 %d\n", __FILE__, status);
@@ -40,9 +41,14 @@
zxlogf(ERROR, "%s: failed to map mmio index 1 %d\n", __FILE__, status);
return status;
}
+ status = pdev.MapMmio(2, &cpu_mmio);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: failed to map mmio index 2 %d\n", __FILE__, status);
+ return status;
+ }
std::unique_ptr<SynClk> device(
- new SynClk(parent, *std::move(global_mmio), *std::move(avio_mmio)));
+ new SynClk(parent, *std::move(global_mmio), *std::move(avio_mmio), *std::move(cpu_mmio)));
status = device->DdkAdd("synaptics-clk");
if (status != ZX_OK) {
@@ -85,6 +91,32 @@
return ZX_OK;
}
+zx_status_t SynClk::CpuSetRate(uint64_t rate) {
+ if (rate < 1'000'000'000 || rate > 1'800'000'000) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ auto dn = static_cast<uint32_t>(rate) / 1'000 * 72 / 1'800'000;
+ CPU_WRP_PLL_REG_ctrl::Get()
+ .FromValue(0)
+ .set_FRAC_READY(1)
+ .set_MODE(0)
+ .set_DN(dn)
+ .set_DM(1)
+ .set_RESETN(1)
+ .WriteTo(&cpu_mmio_);
+ CPU_WRP_PLL_REG_ctrl1::Get().FromValue(0).set_FRAC(0).WriteTo(&cpu_mmio_);
+ CPU_WRP_PLL_REG_ctrl3::Get()
+ .FromValue(0)
+ .set_DP1(1)
+ .set_PDDP1(0)
+ .set_DP(1)
+ .set_PDDP(0)
+ .set_SLOPE(0)
+ .WriteTo(&cpu_mmio_);
+ zxlogf(TRACE, "%s %luHz dn %u dm %u dp %u\n", __FILE__, rate, dn, 1, 1);
+ return ZX_OK;
+}
+
zx_status_t SynClk::AvpllSetRate(bool avpll0, uint64_t rate) {
// rate = (frac / (max_frac+1) + dn) * ref_clk / dm / dp.
// frac = (rate * dp * dm / ref_clk - dn) * (max_frac+1).
@@ -233,6 +265,8 @@
return AvpllSetRate(true, hz);
case as370::kClkAvpll1:
return AvpllSetRate(false, hz);
+ case as370::kClkCpu:
+ return CpuSetRate(hz);
}
return ZX_ERR_NOT_SUPPORTED;
}
@@ -244,6 +278,7 @@
global_mmio_.reset();
avio_mmio_.reset();
+ cpu_mmio_.reset();
DdkRemove();
}
diff --git a/zircon/system/dev/clk/syn-clk/syn-clk.h b/zircon/system/dev/clk/syn-clk/syn-clk.h
index ce3d223..5311a67 100644
--- a/zircon/system/dev/clk/syn-clk/syn-clk.h
+++ b/zircon/system/dev/clk/syn-clk/syn-clk.h
@@ -5,11 +5,12 @@
#ifndef ZIRCON_SYSTEM_DEV_CLK_SYN_CLK_SYN_CLK_H_
#define ZIRCON_SYSTEM_DEV_CLK_SYN_CLK_SYN_CLK_H_
+#include <lib/mmio/mmio.h>
+#include <lib/zircon-internal/thread_annotations.h>
+
#include <ddktl/device.h>
#include <ddktl/protocol/clockimpl.h>
#include <fbl/mutex.h>
-#include <lib/mmio/mmio.h>
-#include <lib/zircon-internal/thread_annotations.h>
#include <soc/as370/as370-clk.h>
namespace clk {
@@ -37,18 +38,22 @@
// Protected for unit tests.
protected:
- SynClk(zx_device_t* parent, ddk::MmioBuffer global_mmio, ddk::MmioBuffer avio_mmio)
+ SynClk(zx_device_t* parent, ddk::MmioBuffer global_mmio, ddk::MmioBuffer avio_mmio,
+ ddk::MmioBuffer cpu_mmio)
: DeviceType(parent),
global_mmio_(std::move(global_mmio)),
- avio_mmio_(std::move(avio_mmio)) {}
+ avio_mmio_(std::move(avio_mmio)),
+ cpu_mmio_(std::move(cpu_mmio)) {}
private:
zx_status_t AvpllClkEnable(bool avpll0, bool enable);
zx_status_t AvpllSetRate(bool avpll0, uint64_t rate);
+ zx_status_t CpuSetRate(uint64_t rate);
fbl::Mutex lock_;
ddk::MmioBuffer global_mmio_ TA_GUARDED(lock_);
ddk::MmioBuffer avio_mmio_ TA_GUARDED(lock_);
+ ddk::MmioBuffer cpu_mmio_ TA_GUARDED(lock_);
};
} // namespace clk
diff --git a/zircon/system/dev/lib/as370/include/soc/as370/as370-clk-regs.h b/zircon/system/dev/lib/as370/include/soc/as370/as370-clk-regs.h
index f3a2e75..1c03901 100644
--- a/zircon/system/dev/lib/as370/include/soc/as370/as370-clk-regs.h
+++ b/zircon/system/dev/lib/as370/include/soc/as370/as370-clk-regs.h
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#pragma once
+#ifndef ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_REGS_H_
+#define ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_REGS_H_
+
+#include <zircon/types.h>
#include <hwreg/bitfields.h>
-#include <zircon/types.h>
// GBL Global Control Registers.
@@ -31,3 +33,38 @@
DEF_BIT(0, ClkEn);
static auto Get() { return hwreg::RegisterAddr<avioSysClk_ctrl>(0x0530); }
};
+
+// CPU Sybsystem Registers.
+
+class CPU_WRP_PLL_REG_ctrl : public hwreg::RegisterBase<CPU_WRP_PLL_REG_ctrl, uint32_t> {
+ public:
+ DEF_BIT(22, FRAC_READY);
+ DEF_BIT(21, READY_BP);
+ DEF_FIELD(20, 19, MODE);
+ DEF_FIELD(18, 8, DN);
+ DEF_FIELD(7, 2, DM);
+ DEF_BIT(1, RESETN);
+ DEF_BIT(0, PD);
+ static auto Get() { return hwreg::RegisterAddr<CPU_WRP_PLL_REG_ctrl>(0x2000); }
+};
+
+struct CPU_WRP_PLL_REG_ctrl1 : public hwreg::RegisterBase<CPU_WRP_PLL_REG_ctrl1, uint32_t> {
+ DEF_FIELD(23, 0, FRAC);
+ static auto Get() { return hwreg::RegisterAddr<CPU_WRP_PLL_REG_ctrl1>(0x2004); }
+};
+
+struct CPU_WRP_PLL_REG_ctrl2 : public hwreg::RegisterBase<CPU_WRP_PLL_REG_ctrl2, uint32_t> {
+ DEF_FIELD(10, 0, SSRATE);
+ static auto Get() { return hwreg::RegisterAddr<CPU_WRP_PLL_REG_ctrl2>(0x2008); }
+};
+
+struct CPU_WRP_PLL_REG_ctrl3 : public hwreg::RegisterBase<CPU_WRP_PLL_REG_ctrl3, uint32_t> {
+ DEF_FIELD(31, 29, DP1);
+ DEF_BIT(28, PDDP1);
+ DEF_FIELD(27, 25, DP);
+ DEF_BIT(24, PDDP);
+ DEF_FIELD(23, 0, SLOPE);
+ static auto Get() { return hwreg::RegisterAddr<CPU_WRP_PLL_REG_ctrl3>(0x200c); }
+};
+
+#endif // ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_REGS_H_
diff --git a/zircon/system/dev/lib/as370/include/soc/as370/as370-clk.h b/zircon/system/dev/lib/as370/include/soc/as370/as370-clk.h
index 6bc3c38..029ac81 100644
--- a/zircon/system/dev/lib/as370/include/soc/as370/as370-clk.h
+++ b/zircon/system/dev/lib/as370/include/soc/as370/as370-clk.h
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#pragma once
+#ifndef ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_H_
+#define ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_H_
namespace as370 {
enum As370Clk {
kClkAvpll0,
kClkAvpll1,
+ kClkCpu,
};
} // namespace as370
+
+#endif // ZIRCON_SYSTEM_DEV_LIB_AS370_INCLUDE_SOC_AS370_AS370_CLK_H_
diff --git a/zircon/system/dev/lib/as370/include/soc/as370/as370-hw.h b/zircon/system/dev/lib/as370/include/soc/as370/as370-hw.h
index 882b182..780be68 100644
--- a/zircon/system/dev/lib/as370/include/soc/as370/as370-hw.h
+++ b/zircon/system/dev/lib/as370/include/soc/as370/as370-hw.h
@@ -25,6 +25,10 @@
constexpr uint32_t kAudioI2sBase = 0xf744'0000;
constexpr uint32_t kAudioI2sSize = fbl::round_up<uint32_t, uint32_t>(0x2'0000, PAGE_SIZE);
+// CPU Sub-System Registers (CPUSS) Registers.
+constexpr uint32_t kCpuBase = 0xf792'0000;
+constexpr uint32_t kCpuSize = fbl::round_up<uint32_t, uint32_t>(0x2'0000, PAGE_SIZE);
+
constexpr uint32_t kDhubIrq = 32 + 11;
} // namespace as370