| // 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 <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 <string> |
| |
| #include <ddk/metadata/power.h> |
| #include <ddk/metadata/pwm.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/lib/metadata/llcpp/vreg.h" |
| #include "vim3-gpios.h" |
| #include "vim3.h" |
| |
| namespace vim3 { |
| |
| namespace { |
| |
| const zx_device_prop_t vreg_props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC}, |
| {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC}, |
| {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_PWM_VREG}, |
| }; |
| |
| constexpr zx_bind_inst_t pwm_ao_d_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PWM), |
| BI_MATCH_IF(EQ, BIND_PWM_ID, A311D_PWM_AO_D), |
| }; |
| |
| constexpr zx_bind_inst_t pwm_a_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PWM), |
| BI_MATCH_IF(EQ, BIND_PWM_ID, A311D_PWM_A), |
| }; |
| |
| constexpr device_fragment_part_t pwm_ao_d_fragment[] = { |
| {std::size(pwm_ao_d_match), pwm_ao_d_match}, |
| }; |
| |
| constexpr device_fragment_part_t pwm_a_fragment[] = { |
| {std::size(pwm_a_match), pwm_a_match}, |
| }; |
| #define PWM_ID(x) #x |
| #define PWM_FRAGMENT_NAME(x) ("pwm-" PWM_ID(x)) |
| constexpr device_fragment_t vreg_fragments[] = { |
| {PWM_FRAGMENT_NAME(A311D_PWM_AO_D), std::size(pwm_ao_d_fragment), pwm_ao_d_fragment}, |
| {PWM_FRAGMENT_NAME(A311D_PWM_A), std::size(pwm_a_fragment), pwm_a_fragment}, |
| }; |
| #undef PWM_FRAGMENT_NAME |
| #undef PWM_ID |
| |
| 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_PROTOCOL, ZX_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_PROTOCOL, ZX_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}, |
| }; |
| |
| 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, ZX_PROTOCOL_POWER_IMPL), |
| }; |
| |
| constexpr device_fragment_part_t power_impl_fragment[] = { |
| {std::size(power_impl_driver_match), power_impl_driver_match}, |
| }; |
| |
| static const pbus_dev_t power_dev = []() { |
| pbus_dev_t 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; |
| }(); |
| |
| zx_device_prop_t power_domain_arm_core_props[] = { |
| {BIND_POWER_DOMAIN_COMPOSITE, 0, PDEV_DID_POWER_DOMAIN_COMPOSITE}, |
| }; |
| |
| constexpr device_fragment_t power_domain_arm_core_fragments[] = { |
| {"power-impl", std::size(power_impl_fragment), power_impl_fragment}, |
| }; |
| |
| constexpr power_domain_t big_domain[] = { |
| {static_cast<uint32_t>(A311dPowerDomains::kArmCoreBig)}, |
| }; |
| |
| constexpr device_metadata_t power_domain_big_core_metadata[] = { |
| { |
| .type = DEVICE_METADATA_POWER_DOMAINS, |
| .data = &big_domain, |
| .length = sizeof(big_domain), |
| }, |
| }; |
| |
| constexpr composite_device_desc_t power_domain_big_core_desc = { |
| .props = power_domain_arm_core_props, |
| .props_count = std::size(power_domain_arm_core_props), |
| .fragments = power_domain_arm_core_fragments, |
| .fragments_count = std::size(power_domain_arm_core_fragments), |
| .primary_fragment = "power-impl", |
| .spawn_colocated = true, |
| .metadata_list = power_domain_big_core_metadata, |
| .metadata_count = std::size(power_domain_big_core_metadata), |
| }; |
| |
| constexpr power_domain_t little_domain[] = { |
| {static_cast<uint32_t>(A311dPowerDomains::kArmCoreLittle)}, |
| }; |
| |
| constexpr device_metadata_t power_domain_little_core_metadata[] = { |
| { |
| .type = DEVICE_METADATA_POWER_DOMAINS, |
| .data = &little_domain, |
| .length = sizeof(little_domain), |
| }, |
| }; |
| |
| constexpr composite_device_desc_t power_domain_little_core_desc = { |
| .props = power_domain_arm_core_props, |
| .props_count = std::size(power_domain_arm_core_props), |
| .fragments = power_domain_arm_core_fragments, |
| .fragments_count = std::size(power_domain_arm_core_fragments), |
| .primary_fragment = "power-impl", |
| .spawn_colocated = true, |
| .metadata_list = power_domain_little_core_metadata, |
| .metadata_count = std::size(power_domain_little_core_metadata), |
| }; |
| |
| zx_device_prop_t fusb302_props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC}, |
| {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC}, |
| {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_FUSB302}, |
| }; |
| |
| constexpr zx_bind_inst_t i2c_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C), |
| BI_ABORT_IF(NE, BIND_I2C_BUS_ID, 0), |
| BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, 0x22), |
| }; |
| |
| constexpr zx_bind_inst_t gpio_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO), |
| BI_MATCH_IF(EQ, BIND_GPIO_PIN, VIM3_FUSB302_INT), |
| }; |
| |
| constexpr device_fragment_part_t i2c_fragment[] = { |
| {std::size(i2c_match), i2c_match}, |
| }; |
| |
| constexpr device_fragment_part_t gpio_fragment[] = { |
| {std::size(gpio_match), gpio_match}, |
| }; |
| |
| constexpr device_fragment_t fusb302_fragments[] = { |
| {"i2c", std::size(i2c_fragment), i2c_fragment}, |
| {"gpio", std::size(gpio_fragment), gpio_fragment}, |
| }; |
| |
| constexpr composite_device_desc_t fusb302_desc = { |
| .props = fusb302_props, |
| .props_count = std::size(fusb302_props), |
| .fragments = fusb302_fragments, |
| .fragments_count = std::size(fusb302_fragments), |
| .primary_fragment = "i2c", |
| .spawn_colocated = true, |
| }; |
| |
| } // 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, std::move(pwm_vreg_entries)); |
| fidl::unstable::OwnedEncodedMessage<vreg::Metadata> encoded_metadata( |
| fidl::internal::WireFormatVersion::kV2, &metadata); |
| if (!encoded_metadata.ok()) { |
| zxlogf(ERROR, "%s: Could not build metadata %s\n", __func__, |
| encoded_metadata.FormatDescription().c_str()); |
| return encoded_metadata.status(); |
| } |
| |
| auto encoded_metadata_bytes = encoded_metadata.GetOutgoingMessage().CopyBytes(); |
| static const device_metadata_t vreg_metadata[] = { |
| { |
| .type = DEVICE_METADATA_VREG, |
| .data = encoded_metadata_bytes.data(), |
| .length = encoded_metadata_bytes.size(), |
| }, |
| }; |
| |
| static composite_device_desc_t vreg_desc = []() { |
| composite_device_desc_t dev = {}; |
| dev.props = vreg_props; |
| dev.props_count = std::size(vreg_props); |
| dev.fragments = vreg_fragments; |
| dev.fragments_count = std::size(vreg_fragments); |
| dev.primary_fragment = vreg_fragments[0].name; // ??? |
| dev.spawn_colocated = true; |
| dev.metadata_list = vreg_metadata; |
| dev.metadata_count = std::size(vreg_metadata); |
| return dev; |
| }(); |
| |
| st = DdkAddComposite("vreg", &vreg_desc); |
| if (st != ZX_OK) { |
| zxlogf(ERROR, "DdkAddComposite for vreg failed, st = %d", st); |
| return st; |
| } |
| |
| st = pbus_.CompositeDeviceAdd(&power_dev, reinterpret_cast<uint64_t>(power_impl_fragments), |
| std::size(power_impl_fragments), nullptr); |
| if (st != ZX_OK) { |
| zxlogf(ERROR, "%s: CompositeDeviceAdd for powerimpl failed, st = %d", __FUNCTION__, st); |
| return st; |
| } |
| |
| st = DdkAddComposite("pd-big-core", &power_domain_big_core_desc); |
| if (st != ZX_OK) { |
| zxlogf(ERROR, "%s: CompositeDeviceAdd for power domain Big Arm Core failed, st = %d", |
| __FUNCTION__, st); |
| return st; |
| } |
| |
| st = DdkAddComposite("pd-little-core", &power_domain_little_core_desc); |
| 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 = DdkAddComposite("fusb302", &fusb302_desc); |
| if (st != ZX_OK) { |
| zxlogf(ERROR, "%s: DdkAddComposite for fusb302 failed, st = %d", __FUNCTION__, st); |
| return st; |
| } |
| |
| return ZX_OK; |
| } |
| |
| } // namespace vim3 |