blob: dfde1fcf73e2466a2e74875c8e1b9326c78213a2 [file] [log] [blame]
// Copyright 2023 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 "post-init.h"
#include <fidl/fuchsia.hardware.gpio/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/inspect/cpp/reader.h>
#include <array>
#include <bind/fuchsia/amlogic/platform/s905d3/cpp/bind.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/gpio/cpp/bind.h>
#include <bind/fuchsia/hardware/spi/cpp/bind.h>
#include <bind/fuchsia/infineon/platform/cpp/bind.h>
#include "sdk/lib/driver/component/cpp/composite_node_spec.h"
#include "sdk/lib/driver/component/cpp/node_add_args.h"
namespace {
constexpr std::array<const char*, 3> kBoardBuildNodeNames = {
"hw-id-0",
"hw-id-1",
"hw-id-2",
};
constexpr std::array<const char*, 2> kBoardOptionNodeNames = {
"hw-id-3",
"hw-id-4",
};
constexpr std::array<const char*, 2> kDisplayIdNodeNames = {
"disp-soc-id0",
"disp-soc-id1",
};
// Nelson Board Revs
enum {
kBoardBuildP1 = 0,
kBoardBuildP2 = 1,
kBoardBuildP2Doe = 2,
kBoardBuildPreEvt = 3,
kBoardBuildEvt = 4,
kBoardBuildDvt = 5,
kBoardBuildDvt2 = 6,
kMaxSupportedBuild, // This must be last entry
};
} // namespace
namespace nelson {
void PostInit::Start(fdf::StartCompleter completer) {
parent_.Bind(std::move(node()));
zx::result pbus =
incoming()->Connect<fuchsia_hardware_platform_bus::Service::PlatformBus>("pbus");
if (pbus.is_error()) {
FDF_LOG(ERROR, "Failed to connect to PlatformBus: %s", pbus.status_string());
return completer(pbus.take_error());
}
pbus_.Bind(*std::move(pbus));
zx::result composite_manager =
incoming()->Connect<fuchsia_driver_framework::CompositeNodeManager>();
if (composite_manager.is_error()) {
FDF_LOG(ERROR, "Failed to connect to CompositeNodeManager: %s",
composite_manager.status_string());
return completer(composite_manager.take_error());
}
composite_manager_ = fidl::SyncClient(*std::move(composite_manager));
auto args = fuchsia_driver_framework::NodeAddArgs({.name = "post-init"});
zx::result controller_endpoints =
fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
if (controller_endpoints.is_error()) {
FDF_LOG(ERROR, "Failed to create controller endpoints: %s",
controller_endpoints.status_string());
return completer(controller_endpoints.take_error());
}
controller_.Bind(std::move(controller_endpoints->client));
if (zx::result result = InitBoardInfo(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = SetInspectProperties(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = InitDisplay(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = SetBoardInfo(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = AddSelinaCompositeNode(); result.is_error()) {
return completer(result.take_error());
}
auto result = parent_->AddChild({std::move(args), std::move(controller_endpoints->server), {}});
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Failed to add child: %s",
result.error_value().framework_error().FormatDescription().c_str());
return completer(zx::error(result.error_value().framework_error().status()));
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Failed to add child");
return completer(zx::error(ZX_ERR_INTERNAL));
}
}
return completer(zx::ok());
}
zx::result<> PostInit::InitBoardInfo() {
zx::result<uint32_t> board_build =
ReadGpios({kBoardBuildNodeNames.data(), kBoardBuildNodeNames.size()});
if (board_build.is_error()) {
return board_build.take_error();
}
FDF_LOG(INFO, "Detected board rev 0x%x", *board_build);
if (*board_build >= kMaxSupportedBuild) {
// We have detected a new board rev. Print this warning just in case the
// new board rev requires additional support that we were not aware of
FDF_LOG(INFO, "Unsupported board revision detected (%u)", *board_build);
}
board_build_ = *board_build;
zx::result<uint32_t> board_option =
ReadGpios({kBoardOptionNodeNames.data(), kBoardOptionNodeNames.size()});
if (board_option.is_error()) {
return board_option.take_error();
}
board_option_ = *board_option;
zx::result<uint32_t> display_id =
ReadGpios({kDisplayIdNodeNames.data(), kDisplayIdNodeNames.size()});
if (display_id.is_error()) {
return display_id.take_error();
}
display_id_ = *display_id;
return zx::ok();
}
zx::result<> PostInit::SetInspectProperties() {
auto inspect_sink = incoming()->Connect<fuchsia_inspect::InspectSink>();
if (inspect_sink.is_error() || !inspect_sink->is_valid()) {
FDF_LOG(ERROR, "Failed to connect to InspectSink: %s", inspect_sink.status_string());
return inspect_sink.take_error();
}
component_inspector_ = std::make_unique<inspect::ComponentInspector>(
dispatcher(), inspect::PublishOptions{.inspector = inspector_});
root_ = inspector_.GetRoot().CreateChild("nelson_board_driver");
board_build_property_ = root_.CreateUint("board_build", board_build_);
board_option_property_ = root_.CreateUint("board_option", board_option_);
display_id_property_ = root_.CreateUint("display_id", display_id_);
return zx::ok();
}
zx::result<uint32_t> PostInit::ReadGpios(cpp20::span<const char* const> node_names) {
uint32_t value = 0;
for (size_t i = 0; i < node_names.size(); i++) {
zx::result gpio = incoming()->Connect<fuchsia_hardware_gpio::Service::Device>(node_names[i]);
if (gpio.is_error()) {
FDF_LOG(ERROR, "Failed to connect to GPIO node: %s", gpio.status_string());
return gpio.take_error();
}
fidl::SyncClient<fuchsia_hardware_gpio::Gpio> gpio_client(*std::move(gpio));
{
auto result = gpio_client->ConfigIn(fuchsia_hardware_gpio::GpioFlags::kNoPull);
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to ConfigIn failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "ConfigIn failed: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
FDF_LOG(ERROR, "Unknown error from call to ConfigIn");
return zx::error(ZX_ERR_BAD_STATE);
}
}
{
auto result = gpio_client->Read();
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to Read failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Read failed: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
FDF_LOG(ERROR, "Unknown error from call to Read");
return zx::error(ZX_ERR_BAD_STATE);
}
if (result->value()) {
value |= 1 << i;
}
}
}
return zx::ok(value);
}
zx::result<> PostInit::SetBoardInfo() {
fdf::Arena arena('PBUS');
auto board_info = fuchsia_hardware_platform_bus::wire::BoardInfo::Builder(arena)
.board_revision(board_build_)
.Build();
auto result = pbus_.buffer(arena)->SetBoardInfo(board_info);
if (!result.ok()) {
FDF_LOG(ERROR, "Call to SetBoardInfo failed: %s", result.FormatDescription().c_str());
return zx::error(result.error().status());
}
if (result->is_error()) {
FDF_LOG(ERROR, "SetBoardInfo failed: %s", zx_status_get_string(result->error_value()));
return result->take_error();
}
return zx::ok();
}
zx::result<> PostInit::AddSelinaCompositeNode() {
if (board_build_ == kBoardBuildP1) {
if (auto result = EnableSelinaOsc(); result.is_error()) {
return result.take_error();
}
}
const std::vector<fuchsia_driver_framework::BindRule> spi_rules{
fdf::MakeAcceptBindRule(bind_fuchsia_hardware_spi::SERVICE,
bind_fuchsia_hardware_spi::SERVICE_ZIRCONTRANSPORT),
fdf::MakeAcceptBindRule(bind_fuchsia::PLATFORM_DEV_VID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_VID_INFINEON),
fdf::MakeAcceptBindRule(bind_fuchsia::PLATFORM_DEV_PID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_PID_BGT60TR13C),
fdf::MakeAcceptBindRule(bind_fuchsia::PLATFORM_DEV_DID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_DID_RADAR_SENSOR),
};
const std::vector<fuchsia_driver_framework::NodeProperty> spi_properties{
fdf::MakeProperty(bind_fuchsia_hardware_spi::SERVICE,
bind_fuchsia_hardware_spi::SERVICE_ZIRCONTRANSPORT),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_VID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_VID_INFINEON),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_PID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_PID_BGT60TR13C),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_DID,
bind_fuchsia_infineon_platform::BIND_PLATFORM_DEV_DID_RADAR_SENSOR),
};
const std::vector<fuchsia_driver_framework::BindRule> irq_gpio_rules{
fdf::MakeAcceptBindRule(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_3),
};
const std::vector<fuchsia_driver_framework::NodeProperty> irq_gpio_properties{
fdf::MakeProperty(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeProperty(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_3),
};
const std::vector<fuchsia_driver_framework::BindRule> reset_gpio_rules{
fdf::MakeAcceptBindRule(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_2),
};
const std::vector<fuchsia_driver_framework::NodeProperty> reset_gpio_properties{
fdf::MakeProperty(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeProperty(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_2),
};
const std::vector<fuchsia_driver_framework::BindRule> cs_gpio_rules{
fdf::MakeAcceptBindRule(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_6),
};
const std::vector<fuchsia_driver_framework::NodeProperty> cs_gpio_properties{
fdf::MakeProperty(bind_fuchsia::PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
fdf::MakeProperty(bind_fuchsia::GPIO_PIN,
bind_fuchsia_amlogic_platform_s905d3::GPIOH_PIN_ID_PIN_6),
};
const std::vector<fuchsia_driver_framework::ParentSpec> selina_parents{
{{spi_rules, spi_properties}},
{{irq_gpio_rules, irq_gpio_properties}},
{{reset_gpio_rules, reset_gpio_properties}},
{{cs_gpio_rules, cs_gpio_properties}},
};
const fuchsia_driver_framework::CompositeNodeSpec selina_node_spec{{
.name = "selina-composite",
.parents = selina_parents,
}};
if (auto result = composite_manager_->AddSpec(selina_node_spec); result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to AddSpec failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "AddSpec failed");
return zx::error(ZX_ERR_INTERNAL);
}
}
return zx::ok();
}
zx::result<> PostInit::EnableSelinaOsc() {
zx::result gpio = incoming()->Connect<fuchsia_hardware_gpio::Service::Device>("selina-osc-en");
if (gpio.is_error()) {
FDF_LOG(ERROR, "Failed to connect to GPIO node: %s", gpio.status_string());
return gpio.take_error();
}
fidl::SyncClient osc_en(*std::move(gpio));
if (auto result = osc_en->SetAltFunction(0); result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to set SELINA_OSC_EN alt function failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Failed to set SELINA_OSC_EN alt function: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
}
if (auto result = osc_en->ConfigIn(fuchsia_hardware_gpio::GpioFlags::kNoPull);
result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to set SELINA_OSC_EN to input failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Failed to set SELINA_OSC_EN to input function: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
}
return zx::ok();
}
} // namespace nelson
FUCHSIA_DRIVER_EXPORT(nelson::PostInit);