blob: c4bf5c9a2ceeac2db8feb4d2873b70cbfc2207ea [file]
// Copyright 2021 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 <fidl/fuchsia.hardware.platform.bus/cpp/driver/fidl.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h>
#include <lib/ddk/binding.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/metadata.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/component/cpp/composite_node_spec.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <string>
#include <bind/fuchsia/amlogic/platform/a311d/cpp/bind.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/gpio/cpp/bind.h>
#include <bind/fuchsia/hardware/pwm/cpp/bind.h>
#include <bind/fuchsia/i2c/cpp/bind.h>
#include <bind/fuchsia/power/cpp/bind.h>
#include <ddk/metadata/power.h>
#include <ddk/metadata/pwm.h>
#include <ddktl/device.h>
#include <soc/aml-a311d/a311d-power.h>
#include <soc/aml-a311d/a311d-pwm.h>
#include <soc/aml-common/aml-power.h>
#include "src/devices/bus/lib/platform-bus-composites/platform-bus-composite.h"
#include "src/devices/lib/metadata/llcpp/vreg.h"
#include "vim3-gpios.h"
#include "vim3.h"
namespace vim3 {
namespace fpbus = fuchsia_hardware_platform_bus;
namespace {
constexpr voltage_pwm_period_ns_t kA311dPwmPeriodNs = 1500;
const uint32_t kVoltageStepUv = 10'000;
static_assert((kMaxVoltageUv - kMinVoltageUv) % kVoltageStepUv == 0,
"Voltage step must be a factor of (kMaxVoltageUv - kMinVoltageUv)\n");
const uint32_t kNumSteps = (kMaxVoltageUv - kMinVoltageUv) / kVoltageStepUv + 1;
enum VregIdx {
PWM_AO_D_VREG,
PWM_A_VREG,
VREG_COUNT,
};
constexpr zx_bind_inst_t vreg_pwm_ao_d_match[] = {
BI_ABORT_IF(NE, BIND_FIDL_PROTOCOL, ZX_FIDL_PROTOCOL_VREG),
BI_MATCH_IF(EQ, BIND_PWM_ID, A311D_PWM_AO_D),
};
constexpr zx_bind_inst_t vreg_pwm_a_match[] = {
BI_ABORT_IF(NE, BIND_FIDL_PROTOCOL, ZX_FIDL_PROTOCOL_VREG),
BI_MATCH_IF(EQ, BIND_PWM_ID, A311D_PWM_A),
};
constexpr device_fragment_part_t vreg_pwm_ao_d_fragment[] = {
{std::size(vreg_pwm_ao_d_match), vreg_pwm_ao_d_match},
};
constexpr device_fragment_part_t vreg_pwm_a_fragment[] = {
{std::size(vreg_pwm_a_match), vreg_pwm_a_match},
};
// Fragments for the "power-impl" composite.
constexpr device_fragment_t power_impl_fragments[] = {
{"vreg-pwm-ao-d", std::size(vreg_pwm_ao_d_fragment), vreg_pwm_ao_d_fragment},
{"vreg-pwm-a", std::size(vreg_pwm_a_fragment), vreg_pwm_a_fragment},
};
constexpr zx_bind_inst_t power_impl_driver_match[] = {
BI_MATCH_IF(EQ, BIND_PROTOCOL, bind_fuchsia_power::BIND_PROTOCOL_IMPL),
};
constexpr device_fragment_part_t core_power_impl_fragment[] = {
{std::size(power_impl_driver_match), power_impl_driver_match},
};
constexpr zx_bind_inst_t core_power_pdev_match_big[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_POWER_CORE),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_INSTANCE_ID, 0),
};
constexpr zx_bind_inst_t core_power_pdev_match_little[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_POWER_CORE),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_INSTANCE_ID, 1),
};
const device_fragment_part_t core_power_pdev_fragment_big[] = {
{std::size(core_power_pdev_match_big), core_power_pdev_match_big},
};
const device_fragment_part_t core_power_pdev_fragment_little[] = {
{std::size(core_power_pdev_match_little), core_power_pdev_match_little},
};
// Fragments for the core power composite.
constexpr device_fragment_t power_domain_core_fragments_big[] = {
{"power-impl", std::size(core_power_impl_fragment), core_power_impl_fragment},
{"pdev", std::size(core_power_pdev_fragment_big), core_power_pdev_fragment_big},
};
constexpr device_fragment_t power_domain_core_fragments_little[] = {
{"power-impl", std::size(core_power_impl_fragment), core_power_impl_fragment},
{"pdev", std::size(core_power_pdev_fragment_little), core_power_pdev_fragment_little},
};
static const fpbus::Node power_dev = []() {
fpbus::Node dev = {};
dev.name() = "aml-power-impl-composite";
dev.vid() = PDEV_VID_AMLOGIC;
dev.pid() = PDEV_PID_AMLOGIC_A311D;
dev.did() = PDEV_DID_AMLOGIC_POWER;
return dev;
}();
constexpr power_domain_t big_domain[] = {
{static_cast<uint32_t>(A311dPowerDomains::kArmCoreBig)},
};
constexpr power_domain_t little_domain[] = {
{static_cast<uint32_t>(A311dPowerDomains::kArmCoreLittle)},
};
const ddk::BindRule kI2cRules[] = {
ddk::MakeAcceptBindRule(bind_fuchsia::FIDL_PROTOCOL,
bind_fuchsia_i2c::BIND_FIDL_PROTOCOL_DEVICE),
ddk::MakeAcceptBindRule(bind_fuchsia::I2C_BUS_ID, bind_fuchsia_i2c::BIND_I2C_BUS_ID_I2C_A0_0),
ddk::MakeAcceptBindRule(bind_fuchsia::I2C_ADDRESS, 0x22u)};
const device_bind_prop_t kI2cProperties[] = {
ddk::MakeProperty(bind_fuchsia::FIDL_PROTOCOL, bind_fuchsia_i2c::BIND_FIDL_PROTOCOL_DEVICE),
};
const ddk::BindRule kGpioRules[] = {
ddk::MakeAcceptBindRule(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
ddk::MakeAcceptBindRule(bind_fuchsia::GPIO_PIN, static_cast<uint32_t>(VIM3_FUSB302_INT))};
const device_bind_prop_t kGpioProperties[] = {
ddk::MakeProperty(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
ddk::MakeProperty(bind_fuchsia_gpio::FUNCTION, bind_fuchsia_gpio::FUNCTION_USB_POWER_DELIVERY)};
zx_status_t AddBigCore(fdf::WireSyncClient<fuchsia_hardware_platform_bus::PlatformBus>& pbus) {
fpbus::Node big_core_dev;
big_core_dev.name() = "pd-big-core";
big_core_dev.vid() = PDEV_VID_GENERIC;
big_core_dev.pid() = PDEV_PID_GENERIC;
big_core_dev.did() = PDEV_DID_POWER_CORE;
big_core_dev.instance_id() = 0;
big_core_dev.metadata() = std::vector<fpbus::Metadata>{
{{
.type = DEVICE_METADATA_POWER_DOMAINS,
.data = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&big_domain),
reinterpret_cast<const uint8_t*>(&big_domain) + sizeof(big_domain)),
}},
};
fidl::Arena<> fidl_arena;
fdf::Arena big_core_arena('BIGC');
auto fragments = platform_bus_composite::MakeFidlFragment(
fidl_arena, power_domain_core_fragments_big, std::size(power_domain_core_fragments_big));
fdf::WireUnownedResult big_core_result =
pbus.buffer(big_core_arena)
->AddComposite(fidl::ToWire(fidl_arena, big_core_dev), fragments, "power-impl");
if (!big_core_result.ok() || big_core_result.value().is_error()) {
zxlogf(ERROR, "AddComposite for big core failed, error = %s",
big_core_result.FormatDescription().c_str());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t AddLittleCore(fdf::WireSyncClient<fuchsia_hardware_platform_bus::PlatformBus>& pbus) {
fpbus::Node little_core_dev;
little_core_dev.name() = "pd-little-core";
little_core_dev.vid() = PDEV_VID_GENERIC;
little_core_dev.pid() = PDEV_PID_GENERIC;
little_core_dev.did() = PDEV_DID_POWER_CORE;
little_core_dev.instance_id() = 1;
little_core_dev.metadata() = std::vector<fpbus::Metadata>{
{{
.type = DEVICE_METADATA_POWER_DOMAINS,
.data = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&little_domain),
reinterpret_cast<const uint8_t*>(&little_domain) + sizeof(little_domain)),
}},
};
fidl::Arena<> fidl_arena;
fdf::Arena little_core_arena('LITC');
auto fragments =
platform_bus_composite::MakeFidlFragment(fidl_arena, power_domain_core_fragments_little,
std::size(power_domain_core_fragments_little));
fdf::WireUnownedResult little_core_result =
pbus.buffer(little_core_arena)
->AddComposite(fidl::ToWire(fidl_arena, little_core_dev), fragments, "power-impl");
if (!little_core_result.ok() || little_core_result.value().is_error()) {
zxlogf(ERROR, "AddComposite for little core failed, error = %s",
little_core_result.FormatDescription().c_str());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
} // namespace
zx_status_t Vim3::PowerInit() {
zx_status_t st;
st = gpio_impl_.ConfigOut(A311D_GPIOE(1), 0);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: ConfigOut failed: %d", __func__, st);
return st;
}
// Configure the GPIO to be Output & set it to alternate
// function 3 which puts in PWM_D mode. A53 cluster (Small)
st = gpio_impl_.SetAltFunction(A311D_GPIOE(1), A311D_GPIOE_1_PWM_D_FN);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: SetAltFunction failed: %d", __func__, st);
return st;
}
st = gpio_impl_.ConfigOut(A311D_GPIOE(2), 0);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: ConfigOut failed: %d", __func__, st);
return st;
}
// Configure the GPIO to be Output & set it to alternate
// function 3 which puts in PWM_D mode. A73 cluster (Big)
st = gpio_impl_.SetAltFunction(A311D_GPIOE(2), A311D_GPIOE_2_PWM_D_FN);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: SetAltFunction failed: %d", __func__, st);
return st;
}
// Add voltage regulator
fidl::Arena<2048> allocator;
fidl::VectorView<vreg::PwmVregMetadataEntry> pwm_vreg_entries(allocator, VREG_COUNT);
pwm_vreg_entries[PWM_AO_D_VREG] = vreg::BuildMetadata(
allocator, A311D_PWM_AO_D, kA311dPwmPeriodNs, kMinVoltageUv, kVoltageStepUv, kNumSteps);
pwm_vreg_entries[PWM_A_VREG] = vreg::BuildMetadata(allocator, A311D_PWM_A, kA311dPwmPeriodNs,
kMinVoltageUv, kVoltageStepUv, kNumSteps);
auto metadata = vreg::BuildMetadata(allocator, pwm_vreg_entries);
fit::result encoded_metadata = fidl::Persist(metadata);
if (!encoded_metadata.is_ok()) {
zxlogf(ERROR, "%s: Could not build metadata %s\n", __func__,
encoded_metadata.error_value().FormatDescription().c_str());
return encoded_metadata.error_value().status();
}
fpbus::Node vreg_dev;
vreg_dev.name() = "vreg";
vreg_dev.vid() = PDEV_VID_GENERIC;
vreg_dev.pid() = PDEV_PID_GENERIC;
vreg_dev.did() = PDEV_DID_PWM_VREG;
vreg_dev.metadata() = std::vector<fpbus::Metadata>{
{{
.type = DEVICE_METADATA_VREG,
.data = encoded_metadata.value(),
}},
};
auto vreg_pwm_9_node = fuchsia_driver_framework::ParentSpec{{
.bind_rules = {fdf::MakeAcceptBindRule(bind_fuchsia::FIDL_PROTOCOL,
bind_fuchsia_hardware_pwm::BIND_FIDL_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule(
bind_fuchsia::PWM_ID,
bind_fuchsia_amlogic_platform_a311d::BIND_PWM_ID_PWM_AO_D)},
.properties = {fdf::MakeProperty(bind_fuchsia::FIDL_PROTOCOL,
bind_fuchsia_hardware_pwm::BIND_FIDL_PROTOCOL_DEVICE),
fdf::MakeProperty(
bind_fuchsia_hardware_pwm::PWM_ID_FUNCTION,
bind_fuchsia_hardware_pwm::PWM_ID_FUNCTION_CORE_POWER_LITTLE_CLUSTER)},
}};
auto vreg_pwm_0_node = fuchsia_driver_framework::ParentSpec{{
.bind_rules = {fdf::MakeAcceptBindRule(bind_fuchsia::FIDL_PROTOCOL,
bind_fuchsia_hardware_pwm::BIND_FIDL_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule(
bind_fuchsia::PWM_ID,
bind_fuchsia_amlogic_platform_a311d::BIND_PWM_ID_PWM_A)},
.properties = {fdf::MakeProperty(bind_fuchsia::FIDL_PROTOCOL,
bind_fuchsia_hardware_pwm::BIND_FIDL_PROTOCOL_DEVICE),
fdf::MakeProperty(
bind_fuchsia_hardware_pwm::PWM_ID_FUNCTION,
bind_fuchsia_hardware_pwm::PWM_ID_FUNCTION_CORE_POWER_BIG_CLUSTER)},
}};
auto vreg_node_spec = fuchsia_driver_framework::CompositeNodeSpec{{
.name = "vreg",
.parents = {{vreg_pwm_9_node, vreg_pwm_0_node}},
}};
fidl::Arena<> fidl_arena;
fdf::Arena vreg_arena('VREG');
fdf::WireUnownedResult vreg_result =
pbus_.buffer(vreg_arena)
->AddCompositeNodeSpec(fidl::ToWire(fidl_arena, vreg_dev),
fidl::ToWire(fidl_arena, vreg_node_spec));
if (!vreg_result.ok() || vreg_result.value().is_error()) {
zxlogf(ERROR, "AddCompositeNodeSpec for vreg failed, error = %s",
vreg_result.FormatDescription().c_str());
return st;
}
fidl_arena.Reset();
fdf::Arena arena('PWR_');
auto result = pbus_.buffer(arena)->AddCompositeImplicitPbusFragment(
fidl::ToWire(fidl_arena, power_dev),
platform_bus_composite::MakeFidlFragment(fidl_arena, power_impl_fragments,
std::size(power_impl_fragments)),
{});
if (!result.ok()) {
zxlogf(ERROR, "%s: AddCompositeImplicitPbusFragment Power(power_dev) request failed: %s",
__func__, result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: AddCompositeImplicitPbusFragment Power(power_dev) failed: %s", __func__,
zx_status_get_string(result->error_value()));
return result->error_value();
}
st = AddBigCore(pbus_);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: CompositeDeviceAdd for power domain Big Arm Core failed, st = %d",
__FUNCTION__, st);
return st;
}
st = AddLittleCore(pbus_);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: CompositeDeviceAdd for power domain Little Arm Core failed, st = %d",
__FUNCTION__, st);
return st;
}
// Add USB power delivery unit
st = DdkAddCompositeNodeSpec(
"fusb302",
ddk::CompositeNodeSpec(kI2cRules, kI2cProperties).AddParentSpec(kGpioRules, kGpioProperties));
if (st != ZX_OK) {
zxlogf(ERROR, "%s: DdkAddCompositeNodeSpec for fusb302 failed, st = %d", __FUNCTION__, st);
return st;
}
return ZX_OK;
}
} // namespace vim3