[wip][nosubmit] Add examples folder
Change-Id: I8ae4d0c620b984322d296e9f813742e3897c7f38
diff --git a/src/devices/example/fidl-tables/aml-cpu.cc b/src/devices/example/fidl-tables/aml-cpu.cc
new file mode 100644
index 0000000..5b20ac8
--- /dev/null
+++ b/src/devices/example/fidl-tables/aml-cpu.cc
@@ -0,0 +1,402 @@
+// 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-cpu.h"
+
+#include <lib/ddk/debug.h>
+#include <lib/ddk/driver.h>
+#include <lib/ddk/platform-defs.h>
+#include <lib/device-protocol/pdev.h>
+#include <lib/inspect/cpp/inspector.h>
+#include <lib/mmio/mmio.h>
+
+#include <memory>
+#include <vector>
+
+#include <ddktl/fidl.h>
+#include <fbl/string_buffer.h>
+
+#include "src/devices/cpu/drivers/aml-cpu/aml-cpu-bind.h"
+
+namespace amlogic_cpu {
+
+namespace {
+// Fragments are provided to this driver in groups of 4. Fragments are provided as follows:
+// [4 fragments for cluster 0]
+// [4 fragments for cluster 1]
+// [...]
+// [4 fragments for cluster n]
+// Each fragment is a combination of the fixed string + id.
+constexpr size_t kFragmentsPerPfDomain = 4;
+
+constexpr zx_off_t kCpuVersionOffset = 0x220;
+
+} // namespace
+
+zx_status_t AmlCpu::Create(void* context, zx_device_t* parent) {
+ zx_status_t st;
+ size_t actual;
+
+ // Get the metadata for the performance domains.
+ size_t perf_domain_size = 0;
+ st = device_get_metadata_size(parent, DEVICE_METADATA_AML_PERF_DOMAINS, &perf_domain_size);
+ zxlogf(DEBUG, "%s: Got AML_PERF_DOMAINS metadata size, st = %d, size = %lu", __func__, st,
+ perf_domain_size);
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get performance domain count from board driver, st = %d", __func__,
+ st);
+ return st;
+ }
+
+ // Make sure that the board driver gave us an exact integer number of performance domains.
+ if (perf_domain_size % sizeof(perf_domain_t) != 0) {
+ zxlogf(ERROR,
+ "%s: Performance domain metadata from board driver is malformed. perf_domain_size = "
+ "%lu, sizeof(perf_domain_t) = %lu",
+ __func__, perf_domain_size, sizeof(perf_domain_t));
+ return ZX_ERR_INTERNAL;
+ }
+
+ const size_t num_perf_domains = perf_domain_size / sizeof(perf_domain_t);
+ std::unique_ptr<perf_domain_t[]> perf_domains =
+ std::make_unique<perf_domain_t[]>(num_perf_domains);
+ st = device_get_metadata(parent, DEVICE_METADATA_AML_PERF_DOMAINS, perf_domains.get(),
+ perf_domain_size, &actual);
+ zxlogf(DEBUG, "%s: Got AML_PERF_DOMAINS metadata, st = %d, actual = %lu", __func__, st, actual);
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get performance domain metadata from board driver, st = %d",
+ __func__, st);
+ return st;
+ }
+ if (actual != perf_domain_size) {
+ zxlogf(ERROR, "%s: Expected %lu bytes in perf domain metadata, got %lu", __func__,
+ perf_domain_size, actual);
+ return ZX_ERR_INTERNAL;
+ }
+
+ size_t op_point_size;
+ st = device_get_metadata_size(parent, DEVICE_METADATA_AML_OP_POINTS, &op_point_size);
+ zxlogf(DEBUG, "%s: Got AML_OP_POINTS metadata size, st = %d, size = %lu", __func__, st,
+ op_point_size);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get opp count metadata size from board driver, st = %d", __func__,
+ st);
+ return st;
+ }
+
+ if (op_point_size % sizeof(operating_point_t) != 0) {
+ zxlogf(ERROR, "%s: Operating point metadata from board driver is malformed", __func__);
+ return ZX_ERR_INTERNAL;
+ }
+
+ const size_t num_op_points = op_point_size / sizeof(operating_point_t);
+ std::unique_ptr<operating_point_t[]> operating_points =
+ std::make_unique<operating_point_t[]>(num_op_points);
+ st = device_get_metadata(parent, DEVICE_METADATA_AML_OP_POINTS, operating_points.get(),
+ op_point_size, &actual);
+ zxlogf(DEBUG, "%s: Got AML_OP_POINTS metadata, st = %d, actual = %lu", __func__, st, actual);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get operating points from board driver, st = %d", __func__, st);
+ return st;
+ }
+ if (actual != op_point_size) {
+ zxlogf(ERROR, "%s: Expected %lu bytes in operating point metadata, got %lu", __func__,
+ op_point_size, actual);
+ return ZX_ERR_INTERNAL;
+ }
+ // Make sure we have the right number of fragments.
+ const uint32_t fragment_count = device_get_fragment_count(parent);
+ zxlogf(DEBUG, "%s: GetFragmentCount = %u", __func__, fragment_count);
+ if ((num_perf_domains * kFragmentsPerPfDomain) + 1 != fragment_count) {
+ zxlogf(ERROR,
+ "%s: Expected %lu fragments for each %lu performance domains for a total of %lu "
+ "fragments but got %u instead",
+ __func__, kFragmentsPerPfDomain, perf_domain_size,
+ perf_domain_size * kFragmentsPerPfDomain, fragment_count);
+ return ZX_ERR_INTERNAL;
+ }
+
+ // Map AOBUS registers
+ auto pdev = ddk::PDev::FromFragment(parent);
+ if (!pdev.is_valid()) {
+ zxlogf(ERROR, "Failed to get platform device fragment");
+ return ZX_ERR_NO_RESOURCES;
+ }
+ std::optional<ddk::MmioBuffer> mmio_buffer;
+ if ((st = pdev.MapMmio(0, &mmio_buffer)) != ZX_OK) {
+ zxlogf(ERROR, "aml-cpu: Failed to map mmio, st = %d", st);
+ return st;
+ }
+ const uint32_t cpu_version_packed = mmio_buffer->Read32(kCpuVersionOffset);
+
+ // Build and publish each performance domain.
+ for (size_t i = 0; i < num_perf_domains; i++) {
+ const perf_domain_t& perf_domain = perf_domains[i];
+
+ fbl::StringBuffer<32> fragment_name;
+ fragment_name.AppendPrintf("clock-pll-div16-%02d", perf_domain.id);
+ ddk::ClockProtocolClient pll_div16_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &pll_div16_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create pll_div_16 clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("clock-cpu-div16-%02d", perf_domain.id);
+ ddk::ClockProtocolClient cpu_div16_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &cpu_div16_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create cpu_div_16 clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("clock-cpu-scaler-%02d", perf_domain.id);
+ ddk::ClockProtocolClient cpu_scaler_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &cpu_scaler_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create cpu_scaler clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("power-%02d", perf_domain.id);
+ ddk::PowerProtocolClient power_client;
+ if ((st = ddk::PowerProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &power_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create power client, st = %d", __func__, st);
+ return st;
+ }
+
+ // Vector of operating points that belong to this power domain.
+ std::vector<operating_point_t> pd_op_points;
+ std::copy_if(operating_points.get(), operating_points.get() + num_op_points,
+ std::back_inserter(pd_op_points), [&perf_domain](const operating_point_t& op) {
+ return op.pd_id == perf_domain.id;
+ });
+
+ // Order operating points from highest frequency to lowest because Operating Point 0 is the
+ // fastest.
+ std::sort(pd_op_points.begin(), pd_op_points.end(),
+ [](const operating_point_t& a, const operating_point_t& b) {
+ return a.freq_hz > b.freq_hz;
+ });
+
+ const size_t perf_state_count = pd_op_points.size();
+ auto perf_states = std::make_unique<device_performance_state_info_t[]>(perf_state_count);
+ for (size_t j = 0; j < perf_state_count; j++) {
+ perf_states[j].state_id = static_cast<uint8_t>(j);
+ perf_states[j].restore_latency = 0;
+ }
+
+ auto device = std::make_unique<AmlCpu>(
+ parent, std::move(pll_div16_client), std::move(cpu_div16_client),
+ std::move(cpu_scaler_client), std::move(power_client), std::move(pd_op_points),
+ perf_domain.core_count);
+
+ st = device->Init();
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to initialize device, st = %d", __func__, st);
+ return st;
+ }
+
+ device->SetCpuInfo(cpu_version_packed);
+
+ st = device->DdkAdd(ddk::DeviceAddArgs("cpu")
+ .set_flags(DEVICE_ADD_NON_BINDABLE)
+ .set_proto_id(ZX_PROTOCOL_CPU_CTRL)
+ .set_performance_states({perf_states.get(), perf_state_count})
+ .set_inspect_vmo(device->inspector_.DuplicateVmo()));
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: DdkAdd failed, st = %d", __func__, st);
+ return st;
+ }
+
+ __UNUSED auto ptr = device.release();
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
+ DdkTransaction transaction(txn);
+ fuchsia_cpuctrl::Device::Dispatch(this, msg, &transaction);
+ return transaction.Status();
+}
+
+void AmlCpu::DdkRelease() { delete this; }
+
+zx_status_t AmlCpu::DdkSetPerformanceState(uint32_t requested_state, uint32_t* out_state) {
+ if (requested_state >= operating_points_.size()) {
+ zxlogf(ERROR, "%s: Requested performance state is out of bounds, state = %u\n", __func__,
+ requested_state);
+ return ZX_ERR_OUT_OF_RANGE;
+ }
+
+ if (!out_state) {
+ zxlogf(ERROR, "%s: out_state may not be null", __func__);
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ // There is no condition under which this function will return ZX_OK but out_state will not
+ // be requested_state so we're going to go ahead and set that up front.
+ *out_state = requested_state;
+
+ const operating_point_t& target_state = operating_points_[requested_state];
+ const operating_point_t& initial_state = operating_points_[current_pstate_];
+
+ zxlogf(INFO, "%s: Scaling from %u MHz %u mV to %u MHz %u mV", __func__,
+ initial_state.freq_hz / 1000000, initial_state.volt_uv / 1000,
+ target_state.freq_hz / 1000000, target_state.volt_uv / 1000);
+
+ if (initial_state.freq_hz == target_state.freq_hz &&
+ initial_state.volt_uv == target_state.volt_uv) {
+ // Nothing to be done.
+ return ZX_OK;
+ }
+
+ zx_status_t st;
+ if (target_state.freq_hz > initial_state.freq_hz) {
+ // If we're increasing the frequency, we need to increase the voltage first.
+ uint32_t actual_voltage;
+ st = pwr_.RequestVoltage(target_state.volt_uv, &actual_voltage);
+ if (st != ZX_OK || actual_voltage != target_state.volt_uv) {
+ zxlogf(ERROR, "%s: Failed to set cpu voltage, requested = %u, got = %u, st = %d", __func__,
+ target_state.volt_uv, actual_voltage, st);
+ return st;
+ }
+ }
+
+ // Set the frequency next.
+ st = cpuscaler_.SetRate(target_state.freq_hz);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Could not set CPU frequency, st = %d\n", __func__, st);
+
+ // Put the voltage back if frequency scaling fails.
+ uint32_t actual_voltage;
+ zx_status_t vt_st = pwr_.RequestVoltage(initial_state.volt_uv, &actual_voltage);
+ if (vt_st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to reset CPU voltage, st = %d, Voltage and frequency mismatch!",
+ __func__, vt_st);
+ return vt_st;
+ }
+ return st;
+ }
+
+ // If we're decreasing the frequency, then we set the voltage after the frequency has
+ // been reduced.
+ if (target_state.freq_hz < initial_state.freq_hz) {
+ // If we're increasing the frequency, we need to increase the voltage first.
+ uint32_t actual_voltage;
+ st = pwr_.RequestVoltage(target_state.volt_uv, &actual_voltage);
+ if (st != ZX_OK || actual_voltage != target_state.volt_uv) {
+ zxlogf(ERROR,
+ "%s: Failed to set cpu voltage, requested = %u, got = %u, st = %d. "
+ "Voltage and frequency mismatch!",
+ __func__, target_state.volt_uv, actual_voltage, st);
+ return st;
+ }
+ }
+
+ zxlogf(INFO, "%s: Success\n", __func__);
+
+ current_pstate_ = requested_state;
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::Init() {
+ zx_status_t result;
+ constexpr uint32_t kInitialPstate = 0;
+
+ result = plldiv16_.Enable();
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to enable plldiv16, st = %d", __func__, result);
+ return result;
+ }
+
+ result = cpudiv16_.Enable();
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to enable cpudiv16_, st = %d", __func__, result);
+ return result;
+ }
+
+ uint32_t min_voltage, max_voltage;
+ pwr_.GetSupportedVoltageRange(&min_voltage, &max_voltage);
+ pwr_.RegisterPowerDomain(min_voltage, max_voltage);
+
+ uint32_t actual;
+ result = DdkSetPerformanceState(kInitialPstate, &actual);
+
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to set initial performance state, st = %d", __func__, result);
+ return result;
+ }
+
+ if (actual != kInitialPstate) {
+ zxlogf(ERROR, "%s: Failed to set initial performance state, requested = %u, actual = %u",
+ __func__, kInitialPstate, actual);
+ return ZX_ERR_INTERNAL;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::DdkConfigureAutoSuspend(bool enable, uint8_t requested_sleep_state) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+void AmlCpu::GetPerformanceStateInfo(uint32_t state,
+ GetPerformanceStateInfoCompleter::Sync& completer) {
+ if (state >= operating_points_.size()) {
+ zxlogf(INFO, "%s: Requested an operating point that's out of bounds, %u\n", __func__, state);
+ completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
+ return;
+ }
+
+ fuchsia_hardware_cpu_ctrl::wire::CpuPerformanceStateInfo result;
+ result.frequency_hz = operating_points_[state].freq_hz;
+ result.voltage_uv = operating_points_[state].volt_uv;
+
+ completer.ReplySuccess(result);
+}
+
+void AmlCpu::GetNumLogicalCores(GetNumLogicalCoresCompleter::Sync& completer) {
+ completer.Reply(core_count_);
+}
+
+void AmlCpu::GetLogicalCoreId(uint64_t index, GetLogicalCoreIdCompleter::Sync& completer) {
+ // Placeholder.
+ completer.Reply(0);
+}
+
+void AmlCpu::SetCpuInfo(uint32_t cpu_version_packed) {
+ const uint8_t major_revision = (cpu_version_packed >> 24) & 0xff;
+ const uint8_t minor_revision = (cpu_version_packed >> 8) & 0xff;
+ const uint8_t cpu_package_id = (cpu_version_packed >> 20) & 0x0f;
+ zxlogf(INFO, "major revision number: 0x%x", major_revision);
+ zxlogf(INFO, "minor revision number: 0x%x", minor_revision);
+ zxlogf(INFO, "cpu package id number: 0x%x", cpu_package_id);
+
+ cpu_info_.CreateUint("cpu_major_revision", major_revision, &inspector_);
+ cpu_info_.CreateUint("cpu_minor_revision", minor_revision, &inspector_);
+ cpu_info_.CreateUint("cpu_package_id", cpu_package_id, &inspector_);
+}
+
+} // namespace amlogic_cpu
+
+static constexpr zx_driver_ops_t aml_cpu_driver_ops = []() {
+ zx_driver_ops_t result = {};
+ result.version = DRIVER_OPS_VERSION;
+ result.bind = amlogic_cpu::AmlCpu::Create;
+ return result;
+}();
+
+// clang-format off
+ZIRCON_DRIVER(aml_cpu, aml_cpu_driver_ops, "zircon", "0.1");
+
diff --git a/src/devices/example/fidl-tables/astro-cpu.cc b/src/devices/example/fidl-tables/astro-cpu.cc
new file mode 100644
index 0000000..944128c
--- /dev/null
+++ b/src/devices/example/fidl-tables/astro-cpu.cc
@@ -0,0 +1,156 @@
+// 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 <fuchsia/hardware/platform/bus/c/banjo.h>
+#include <lib/ddk/binding.h>
+#include <lib/ddk/debug.h>
+#include <lib/ddk/device.h>
+#include <lib/ddk/driver.h>
+#include <lib/ddk/platform-defs.h>
+
+#include <soc/aml-common/aml-cpu-metadata.h>
+#include <soc/aml-meson/g12a-clk.h>
+#include <soc/aml-s905d2/s905d2-hw.h>
+#include <soc/aml-s905d2/s905d2-power.h>
+
+#include "astro-gpios.h"
+#include "astro.h"
+
+namespace {
+
+constexpr amlogic_cpu::PerfDomainId kPdArmA53 = 1;
+
+constexpr pbus_mmio_t cpu_mmios[]{
+ {
+ // AOBUS
+ .base = S905D2_AOBUS_BASE,
+ .length = S905D2_AOBUS_LENGTH,
+ },
+};
+
+constexpr zx_bind_inst_t root_match[] = {
+ BI_MATCH(),
+};
+
+constexpr zx_bind_inst_t power_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_POWER),
+ BI_MATCH_IF(EQ, BIND_POWER_DOMAIN, static_cast<uint32_t>(S905d2PowerDomains::kArmCore)),
+};
+
+constexpr device_fragment_part_t power_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(power_match), power_match},
+};
+
+constexpr zx_bind_inst_t clock_pll_div16_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_PLL_DIV16),
+};
+
+constexpr device_fragment_part_t clock_pll_div16_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_pll_div16_match), clock_pll_div16_match},
+};
+
+constexpr zx_bind_inst_t clock_cpu_div16_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_CPU_CLK_DIV16),
+};
+
+constexpr device_fragment_part_t clock_cpu_div16_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_cpu_div16_match), clock_cpu_div16_match},
+};
+
+constexpr zx_bind_inst_t clock_cpu_scaler_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_CPU_CLK),
+};
+
+constexpr device_fragment_part_t clock_cpu_scaler_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_cpu_scaler_match), clock_cpu_scaler_match},
+};
+
+constexpr device_fragment_t fragments[] = {
+ {"power-01", countof(power_dfp), power_dfp},
+ {"clock-pll-div16-01", countof(clock_pll_div16_dfp), clock_pll_div16_dfp},
+ {"clock-cpu-div16-01", countof(clock_cpu_div16_dfp), clock_cpu_div16_dfp},
+ {"clock-cpu-scaler-01", countof(clock_cpu_scaler_dfp), clock_cpu_scaler_dfp},
+};
+
+constexpr amlogic_cpu::operating_point_t operating_points[] = {
+ {.freq_hz = 100'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 250'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 500'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 667'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'000'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'200'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'398'000'000, .volt_uv = 761'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'512'000'000, .volt_uv = 791'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'608'000'000, .volt_uv = 831'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'704'000'000, .volt_uv = 861'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'896'000'000, .volt_uv = 981'000, .pd_id = kPdArmA53},
+};
+
+constexpr amlogic_cpu::perf_domain_t performance_domains[] = {
+ {.id = kPdArmA53, .core_count = 4, .relative_performance = 255, .name = "S905D2 ARM A53"},
+};
+
+static const pbus_metadata_t cpu_metadata[] = {
+ {
+ .type = DEVICE_METADATA_AML_OP_POINTS,
+ .data_buffer = reinterpret_cast<const uint8_t*>(operating_points),
+ .data_size = sizeof(operating_points),
+ },
+ {
+ .type = DEVICE_METADATA_AML_PERF_DOMAINS,
+ .data_buffer = reinterpret_cast<const uint8_t*>(performance_domains),
+ .data_size = sizeof(performance_domains),
+ },
+};
+
+constexpr pbus_dev_t cpu_dev = []() {
+ pbus_dev_t result = {};
+ result.name = "aml-cpu";
+ result.vid = PDEV_VID_GOOGLE;
+ result.pid = PDEV_PID_ASTRO;
+ result.did = PDEV_DID_GOOGLE_AMLOGIC_CPU;
+ result.metadata_list = cpu_metadata;
+ result.metadata_count = countof(cpu_metadata);
+ result.mmio_list = cpu_mmios;
+ result.mmio_count = countof(cpu_mmios);
+ return result;
+}();
+
+} // namespace
+
+namespace astro {
+
+zx_status_t Astro::CpuInit() {
+ zx_status_t result;
+ result = gpio_impl_.ConfigOut(S905D2_PWM_D_PIN, 0);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: ConfigOut failed: %d", __func__, result);
+ return result;
+ }
+
+ // Configure the GPIO to be Output & set it to alternate
+ // function 3 which puts in PWM_D mode.
+ result = gpio_impl_.SetAltFunction(S905D2_PWM_D_PIN, S905D2_PWM_D_FN);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: SetAltFunction failed: %d", __func__, result);
+ return result;
+ }
+
+ result = pbus_.CompositeDeviceAdd(&cpu_dev, reinterpret_cast<uint64_t>(fragments),
+ countof(fragments), 1);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to add CPU composite device, st = %d", __func__, result);
+ }
+
+ return result;
+}
+
+} // namespace astro
diff --git a/src/devices/example/flatbuffers/aml-cpu.cc b/src/devices/example/flatbuffers/aml-cpu.cc
new file mode 100644
index 0000000..5b20ac8
--- /dev/null
+++ b/src/devices/example/flatbuffers/aml-cpu.cc
@@ -0,0 +1,402 @@
+// 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-cpu.h"
+
+#include <lib/ddk/debug.h>
+#include <lib/ddk/driver.h>
+#include <lib/ddk/platform-defs.h>
+#include <lib/device-protocol/pdev.h>
+#include <lib/inspect/cpp/inspector.h>
+#include <lib/mmio/mmio.h>
+
+#include <memory>
+#include <vector>
+
+#include <ddktl/fidl.h>
+#include <fbl/string_buffer.h>
+
+#include "src/devices/cpu/drivers/aml-cpu/aml-cpu-bind.h"
+
+namespace amlogic_cpu {
+
+namespace {
+// Fragments are provided to this driver in groups of 4. Fragments are provided as follows:
+// [4 fragments for cluster 0]
+// [4 fragments for cluster 1]
+// [...]
+// [4 fragments for cluster n]
+// Each fragment is a combination of the fixed string + id.
+constexpr size_t kFragmentsPerPfDomain = 4;
+
+constexpr zx_off_t kCpuVersionOffset = 0x220;
+
+} // namespace
+
+zx_status_t AmlCpu::Create(void* context, zx_device_t* parent) {
+ zx_status_t st;
+ size_t actual;
+
+ // Get the metadata for the performance domains.
+ size_t perf_domain_size = 0;
+ st = device_get_metadata_size(parent, DEVICE_METADATA_AML_PERF_DOMAINS, &perf_domain_size);
+ zxlogf(DEBUG, "%s: Got AML_PERF_DOMAINS metadata size, st = %d, size = %lu", __func__, st,
+ perf_domain_size);
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get performance domain count from board driver, st = %d", __func__,
+ st);
+ return st;
+ }
+
+ // Make sure that the board driver gave us an exact integer number of performance domains.
+ if (perf_domain_size % sizeof(perf_domain_t) != 0) {
+ zxlogf(ERROR,
+ "%s: Performance domain metadata from board driver is malformed. perf_domain_size = "
+ "%lu, sizeof(perf_domain_t) = %lu",
+ __func__, perf_domain_size, sizeof(perf_domain_t));
+ return ZX_ERR_INTERNAL;
+ }
+
+ const size_t num_perf_domains = perf_domain_size / sizeof(perf_domain_t);
+ std::unique_ptr<perf_domain_t[]> perf_domains =
+ std::make_unique<perf_domain_t[]>(num_perf_domains);
+ st = device_get_metadata(parent, DEVICE_METADATA_AML_PERF_DOMAINS, perf_domains.get(),
+ perf_domain_size, &actual);
+ zxlogf(DEBUG, "%s: Got AML_PERF_DOMAINS metadata, st = %d, actual = %lu", __func__, st, actual);
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get performance domain metadata from board driver, st = %d",
+ __func__, st);
+ return st;
+ }
+ if (actual != perf_domain_size) {
+ zxlogf(ERROR, "%s: Expected %lu bytes in perf domain metadata, got %lu", __func__,
+ perf_domain_size, actual);
+ return ZX_ERR_INTERNAL;
+ }
+
+ size_t op_point_size;
+ st = device_get_metadata_size(parent, DEVICE_METADATA_AML_OP_POINTS, &op_point_size);
+ zxlogf(DEBUG, "%s: Got AML_OP_POINTS metadata size, st = %d, size = %lu", __func__, st,
+ op_point_size);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get opp count metadata size from board driver, st = %d", __func__,
+ st);
+ return st;
+ }
+
+ if (op_point_size % sizeof(operating_point_t) != 0) {
+ zxlogf(ERROR, "%s: Operating point metadata from board driver is malformed", __func__);
+ return ZX_ERR_INTERNAL;
+ }
+
+ const size_t num_op_points = op_point_size / sizeof(operating_point_t);
+ std::unique_ptr<operating_point_t[]> operating_points =
+ std::make_unique<operating_point_t[]>(num_op_points);
+ st = device_get_metadata(parent, DEVICE_METADATA_AML_OP_POINTS, operating_points.get(),
+ op_point_size, &actual);
+ zxlogf(DEBUG, "%s: Got AML_OP_POINTS metadata, st = %d, actual = %lu", __func__, st, actual);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to get operating points from board driver, st = %d", __func__, st);
+ return st;
+ }
+ if (actual != op_point_size) {
+ zxlogf(ERROR, "%s: Expected %lu bytes in operating point metadata, got %lu", __func__,
+ op_point_size, actual);
+ return ZX_ERR_INTERNAL;
+ }
+ // Make sure we have the right number of fragments.
+ const uint32_t fragment_count = device_get_fragment_count(parent);
+ zxlogf(DEBUG, "%s: GetFragmentCount = %u", __func__, fragment_count);
+ if ((num_perf_domains * kFragmentsPerPfDomain) + 1 != fragment_count) {
+ zxlogf(ERROR,
+ "%s: Expected %lu fragments for each %lu performance domains for a total of %lu "
+ "fragments but got %u instead",
+ __func__, kFragmentsPerPfDomain, perf_domain_size,
+ perf_domain_size * kFragmentsPerPfDomain, fragment_count);
+ return ZX_ERR_INTERNAL;
+ }
+
+ // Map AOBUS registers
+ auto pdev = ddk::PDev::FromFragment(parent);
+ if (!pdev.is_valid()) {
+ zxlogf(ERROR, "Failed to get platform device fragment");
+ return ZX_ERR_NO_RESOURCES;
+ }
+ std::optional<ddk::MmioBuffer> mmio_buffer;
+ if ((st = pdev.MapMmio(0, &mmio_buffer)) != ZX_OK) {
+ zxlogf(ERROR, "aml-cpu: Failed to map mmio, st = %d", st);
+ return st;
+ }
+ const uint32_t cpu_version_packed = mmio_buffer->Read32(kCpuVersionOffset);
+
+ // Build and publish each performance domain.
+ for (size_t i = 0; i < num_perf_domains; i++) {
+ const perf_domain_t& perf_domain = perf_domains[i];
+
+ fbl::StringBuffer<32> fragment_name;
+ fragment_name.AppendPrintf("clock-pll-div16-%02d", perf_domain.id);
+ ddk::ClockProtocolClient pll_div16_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &pll_div16_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create pll_div_16 clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("clock-cpu-div16-%02d", perf_domain.id);
+ ddk::ClockProtocolClient cpu_div16_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &cpu_div16_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create cpu_div_16 clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("clock-cpu-scaler-%02d", perf_domain.id);
+ ddk::ClockProtocolClient cpu_scaler_client;
+ if ((st = ddk::ClockProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &cpu_scaler_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create cpu_scaler clock client, st = %d", __func__, st);
+ return st;
+ }
+
+ fragment_name.Resize(0);
+ fragment_name.AppendPrintf("power-%02d", perf_domain.id);
+ ddk::PowerProtocolClient power_client;
+ if ((st = ddk::PowerProtocolClient::CreateFromDevice(parent, fragment_name.c_str(),
+ &power_client)) != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to create power client, st = %d", __func__, st);
+ return st;
+ }
+
+ // Vector of operating points that belong to this power domain.
+ std::vector<operating_point_t> pd_op_points;
+ std::copy_if(operating_points.get(), operating_points.get() + num_op_points,
+ std::back_inserter(pd_op_points), [&perf_domain](const operating_point_t& op) {
+ return op.pd_id == perf_domain.id;
+ });
+
+ // Order operating points from highest frequency to lowest because Operating Point 0 is the
+ // fastest.
+ std::sort(pd_op_points.begin(), pd_op_points.end(),
+ [](const operating_point_t& a, const operating_point_t& b) {
+ return a.freq_hz > b.freq_hz;
+ });
+
+ const size_t perf_state_count = pd_op_points.size();
+ auto perf_states = std::make_unique<device_performance_state_info_t[]>(perf_state_count);
+ for (size_t j = 0; j < perf_state_count; j++) {
+ perf_states[j].state_id = static_cast<uint8_t>(j);
+ perf_states[j].restore_latency = 0;
+ }
+
+ auto device = std::make_unique<AmlCpu>(
+ parent, std::move(pll_div16_client), std::move(cpu_div16_client),
+ std::move(cpu_scaler_client), std::move(power_client), std::move(pd_op_points),
+ perf_domain.core_count);
+
+ st = device->Init();
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to initialize device, st = %d", __func__, st);
+ return st;
+ }
+
+ device->SetCpuInfo(cpu_version_packed);
+
+ st = device->DdkAdd(ddk::DeviceAddArgs("cpu")
+ .set_flags(DEVICE_ADD_NON_BINDABLE)
+ .set_proto_id(ZX_PROTOCOL_CPU_CTRL)
+ .set_performance_states({perf_states.get(), perf_state_count})
+ .set_inspect_vmo(device->inspector_.DuplicateVmo()));
+
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: DdkAdd failed, st = %d", __func__, st);
+ return st;
+ }
+
+ __UNUSED auto ptr = device.release();
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
+ DdkTransaction transaction(txn);
+ fuchsia_cpuctrl::Device::Dispatch(this, msg, &transaction);
+ return transaction.Status();
+}
+
+void AmlCpu::DdkRelease() { delete this; }
+
+zx_status_t AmlCpu::DdkSetPerformanceState(uint32_t requested_state, uint32_t* out_state) {
+ if (requested_state >= operating_points_.size()) {
+ zxlogf(ERROR, "%s: Requested performance state is out of bounds, state = %u\n", __func__,
+ requested_state);
+ return ZX_ERR_OUT_OF_RANGE;
+ }
+
+ if (!out_state) {
+ zxlogf(ERROR, "%s: out_state may not be null", __func__);
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ // There is no condition under which this function will return ZX_OK but out_state will not
+ // be requested_state so we're going to go ahead and set that up front.
+ *out_state = requested_state;
+
+ const operating_point_t& target_state = operating_points_[requested_state];
+ const operating_point_t& initial_state = operating_points_[current_pstate_];
+
+ zxlogf(INFO, "%s: Scaling from %u MHz %u mV to %u MHz %u mV", __func__,
+ initial_state.freq_hz / 1000000, initial_state.volt_uv / 1000,
+ target_state.freq_hz / 1000000, target_state.volt_uv / 1000);
+
+ if (initial_state.freq_hz == target_state.freq_hz &&
+ initial_state.volt_uv == target_state.volt_uv) {
+ // Nothing to be done.
+ return ZX_OK;
+ }
+
+ zx_status_t st;
+ if (target_state.freq_hz > initial_state.freq_hz) {
+ // If we're increasing the frequency, we need to increase the voltage first.
+ uint32_t actual_voltage;
+ st = pwr_.RequestVoltage(target_state.volt_uv, &actual_voltage);
+ if (st != ZX_OK || actual_voltage != target_state.volt_uv) {
+ zxlogf(ERROR, "%s: Failed to set cpu voltage, requested = %u, got = %u, st = %d", __func__,
+ target_state.volt_uv, actual_voltage, st);
+ return st;
+ }
+ }
+
+ // Set the frequency next.
+ st = cpuscaler_.SetRate(target_state.freq_hz);
+ if (st != ZX_OK) {
+ zxlogf(ERROR, "%s: Could not set CPU frequency, st = %d\n", __func__, st);
+
+ // Put the voltage back if frequency scaling fails.
+ uint32_t actual_voltage;
+ zx_status_t vt_st = pwr_.RequestVoltage(initial_state.volt_uv, &actual_voltage);
+ if (vt_st != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to reset CPU voltage, st = %d, Voltage and frequency mismatch!",
+ __func__, vt_st);
+ return vt_st;
+ }
+ return st;
+ }
+
+ // If we're decreasing the frequency, then we set the voltage after the frequency has
+ // been reduced.
+ if (target_state.freq_hz < initial_state.freq_hz) {
+ // If we're increasing the frequency, we need to increase the voltage first.
+ uint32_t actual_voltage;
+ st = pwr_.RequestVoltage(target_state.volt_uv, &actual_voltage);
+ if (st != ZX_OK || actual_voltage != target_state.volt_uv) {
+ zxlogf(ERROR,
+ "%s: Failed to set cpu voltage, requested = %u, got = %u, st = %d. "
+ "Voltage and frequency mismatch!",
+ __func__, target_state.volt_uv, actual_voltage, st);
+ return st;
+ }
+ }
+
+ zxlogf(INFO, "%s: Success\n", __func__);
+
+ current_pstate_ = requested_state;
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::Init() {
+ zx_status_t result;
+ constexpr uint32_t kInitialPstate = 0;
+
+ result = plldiv16_.Enable();
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to enable plldiv16, st = %d", __func__, result);
+ return result;
+ }
+
+ result = cpudiv16_.Enable();
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to enable cpudiv16_, st = %d", __func__, result);
+ return result;
+ }
+
+ uint32_t min_voltage, max_voltage;
+ pwr_.GetSupportedVoltageRange(&min_voltage, &max_voltage);
+ pwr_.RegisterPowerDomain(min_voltage, max_voltage);
+
+ uint32_t actual;
+ result = DdkSetPerformanceState(kInitialPstate, &actual);
+
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to set initial performance state, st = %d", __func__, result);
+ return result;
+ }
+
+ if (actual != kInitialPstate) {
+ zxlogf(ERROR, "%s: Failed to set initial performance state, requested = %u, actual = %u",
+ __func__, kInitialPstate, actual);
+ return ZX_ERR_INTERNAL;
+ }
+
+ return ZX_OK;
+}
+
+zx_status_t AmlCpu::DdkConfigureAutoSuspend(bool enable, uint8_t requested_sleep_state) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+void AmlCpu::GetPerformanceStateInfo(uint32_t state,
+ GetPerformanceStateInfoCompleter::Sync& completer) {
+ if (state >= operating_points_.size()) {
+ zxlogf(INFO, "%s: Requested an operating point that's out of bounds, %u\n", __func__, state);
+ completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
+ return;
+ }
+
+ fuchsia_hardware_cpu_ctrl::wire::CpuPerformanceStateInfo result;
+ result.frequency_hz = operating_points_[state].freq_hz;
+ result.voltage_uv = operating_points_[state].volt_uv;
+
+ completer.ReplySuccess(result);
+}
+
+void AmlCpu::GetNumLogicalCores(GetNumLogicalCoresCompleter::Sync& completer) {
+ completer.Reply(core_count_);
+}
+
+void AmlCpu::GetLogicalCoreId(uint64_t index, GetLogicalCoreIdCompleter::Sync& completer) {
+ // Placeholder.
+ completer.Reply(0);
+}
+
+void AmlCpu::SetCpuInfo(uint32_t cpu_version_packed) {
+ const uint8_t major_revision = (cpu_version_packed >> 24) & 0xff;
+ const uint8_t minor_revision = (cpu_version_packed >> 8) & 0xff;
+ const uint8_t cpu_package_id = (cpu_version_packed >> 20) & 0x0f;
+ zxlogf(INFO, "major revision number: 0x%x", major_revision);
+ zxlogf(INFO, "minor revision number: 0x%x", minor_revision);
+ zxlogf(INFO, "cpu package id number: 0x%x", cpu_package_id);
+
+ cpu_info_.CreateUint("cpu_major_revision", major_revision, &inspector_);
+ cpu_info_.CreateUint("cpu_minor_revision", minor_revision, &inspector_);
+ cpu_info_.CreateUint("cpu_package_id", cpu_package_id, &inspector_);
+}
+
+} // namespace amlogic_cpu
+
+static constexpr zx_driver_ops_t aml_cpu_driver_ops = []() {
+ zx_driver_ops_t result = {};
+ result.version = DRIVER_OPS_VERSION;
+ result.bind = amlogic_cpu::AmlCpu::Create;
+ return result;
+}();
+
+// clang-format off
+ZIRCON_DRIVER(aml_cpu, aml_cpu_driver_ops, "zircon", "0.1");
+
diff --git a/src/devices/example/flatbuffers/astro-cpu.cc b/src/devices/example/flatbuffers/astro-cpu.cc
new file mode 100644
index 0000000..944128c
--- /dev/null
+++ b/src/devices/example/flatbuffers/astro-cpu.cc
@@ -0,0 +1,156 @@
+// 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 <fuchsia/hardware/platform/bus/c/banjo.h>
+#include <lib/ddk/binding.h>
+#include <lib/ddk/debug.h>
+#include <lib/ddk/device.h>
+#include <lib/ddk/driver.h>
+#include <lib/ddk/platform-defs.h>
+
+#include <soc/aml-common/aml-cpu-metadata.h>
+#include <soc/aml-meson/g12a-clk.h>
+#include <soc/aml-s905d2/s905d2-hw.h>
+#include <soc/aml-s905d2/s905d2-power.h>
+
+#include "astro-gpios.h"
+#include "astro.h"
+
+namespace {
+
+constexpr amlogic_cpu::PerfDomainId kPdArmA53 = 1;
+
+constexpr pbus_mmio_t cpu_mmios[]{
+ {
+ // AOBUS
+ .base = S905D2_AOBUS_BASE,
+ .length = S905D2_AOBUS_LENGTH,
+ },
+};
+
+constexpr zx_bind_inst_t root_match[] = {
+ BI_MATCH(),
+};
+
+constexpr zx_bind_inst_t power_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_POWER),
+ BI_MATCH_IF(EQ, BIND_POWER_DOMAIN, static_cast<uint32_t>(S905d2PowerDomains::kArmCore)),
+};
+
+constexpr device_fragment_part_t power_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(power_match), power_match},
+};
+
+constexpr zx_bind_inst_t clock_pll_div16_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_PLL_DIV16),
+};
+
+constexpr device_fragment_part_t clock_pll_div16_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_pll_div16_match), clock_pll_div16_match},
+};
+
+constexpr zx_bind_inst_t clock_cpu_div16_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_CPU_CLK_DIV16),
+};
+
+constexpr device_fragment_part_t clock_cpu_div16_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_cpu_div16_match), clock_cpu_div16_match},
+};
+
+constexpr zx_bind_inst_t clock_cpu_scaler_match[] = {
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_CLOCK),
+ BI_MATCH_IF(EQ, BIND_CLOCK_ID, g12a_clk::CLK_SYS_CPU_CLK),
+};
+
+constexpr device_fragment_part_t clock_cpu_scaler_dfp[] = {
+ {countof(root_match), root_match},
+ {countof(clock_cpu_scaler_match), clock_cpu_scaler_match},
+};
+
+constexpr device_fragment_t fragments[] = {
+ {"power-01", countof(power_dfp), power_dfp},
+ {"clock-pll-div16-01", countof(clock_pll_div16_dfp), clock_pll_div16_dfp},
+ {"clock-cpu-div16-01", countof(clock_cpu_div16_dfp), clock_cpu_div16_dfp},
+ {"clock-cpu-scaler-01", countof(clock_cpu_scaler_dfp), clock_cpu_scaler_dfp},
+};
+
+constexpr amlogic_cpu::operating_point_t operating_points[] = {
+ {.freq_hz = 100'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 250'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 500'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 667'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'000'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'200'000'000, .volt_uv = 731'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'398'000'000, .volt_uv = 761'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'512'000'000, .volt_uv = 791'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'608'000'000, .volt_uv = 831'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'704'000'000, .volt_uv = 861'000, .pd_id = kPdArmA53},
+ {.freq_hz = 1'896'000'000, .volt_uv = 981'000, .pd_id = kPdArmA53},
+};
+
+constexpr amlogic_cpu::perf_domain_t performance_domains[] = {
+ {.id = kPdArmA53, .core_count = 4, .relative_performance = 255, .name = "S905D2 ARM A53"},
+};
+
+static const pbus_metadata_t cpu_metadata[] = {
+ {
+ .type = DEVICE_METADATA_AML_OP_POINTS,
+ .data_buffer = reinterpret_cast<const uint8_t*>(operating_points),
+ .data_size = sizeof(operating_points),
+ },
+ {
+ .type = DEVICE_METADATA_AML_PERF_DOMAINS,
+ .data_buffer = reinterpret_cast<const uint8_t*>(performance_domains),
+ .data_size = sizeof(performance_domains),
+ },
+};
+
+constexpr pbus_dev_t cpu_dev = []() {
+ pbus_dev_t result = {};
+ result.name = "aml-cpu";
+ result.vid = PDEV_VID_GOOGLE;
+ result.pid = PDEV_PID_ASTRO;
+ result.did = PDEV_DID_GOOGLE_AMLOGIC_CPU;
+ result.metadata_list = cpu_metadata;
+ result.metadata_count = countof(cpu_metadata);
+ result.mmio_list = cpu_mmios;
+ result.mmio_count = countof(cpu_mmios);
+ return result;
+}();
+
+} // namespace
+
+namespace astro {
+
+zx_status_t Astro::CpuInit() {
+ zx_status_t result;
+ result = gpio_impl_.ConfigOut(S905D2_PWM_D_PIN, 0);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: ConfigOut failed: %d", __func__, result);
+ return result;
+ }
+
+ // Configure the GPIO to be Output & set it to alternate
+ // function 3 which puts in PWM_D mode.
+ result = gpio_impl_.SetAltFunction(S905D2_PWM_D_PIN, S905D2_PWM_D_FN);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: SetAltFunction failed: %d", __func__, result);
+ return result;
+ }
+
+ result = pbus_.CompositeDeviceAdd(&cpu_dev, reinterpret_cast<uint64_t>(fragments),
+ countof(fragments), 1);
+ if (result != ZX_OK) {
+ zxlogf(ERROR, "%s: Failed to add CPU composite device, st = %d", __func__, result);
+ }
+
+ return result;
+}
+
+} // namespace astro