blob: ac89583a4aeb82af0f13d7f7a289bbfda7bdce46 [file] [log] [blame]
// 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 <fidl/fuchsia.hardware.platform.bus/cpp/driver/fidl.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h>
#include <fidl/fuchsia.scheduler/cpp/fidl.h>
#include <lib/ddk/binding.h>
#include <lib/ddk/debug.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 <lib/mmio/mmio.h>
#include <lib/zx/time.h>
#include <bind/fuchsia/amlogic/platform/cpp/bind.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/gpio/cpp/bind.h>
#include <bind/fuchsia/hardware/gpio/cpp/bind.h>
#include <bind/fuchsia/hardware/registers/cpp/bind.h>
#include <bind/fuchsia/platform/cpp/bind.h>
#include <bind/fuchsia/register/cpp/bind.h>
#include <fbl/algorithm.h>
#include <soc/aml-common/aml-registers.h>
#include <soc/aml-common/aml-spi.h>
#include <soc/aml-s905d2/s905d2-gpio.h>
#include <soc/aml-s905d3/s905d3-hw.h>
#include "nelson-gpios.h"
#include "nelson.h"
#include "src/devices/lib/fidl-metadata/spi.h"
#define HHI_SPICC_CLK_CNTL (0xf7 * 4)
#define spicc0_clk_sel_fclk_div4 (2 << 7)
#define spicc0_clk_en (1 << 6)
#define spicc0_clk_div(x) ((x) - 1)
#define spicc1_clk_sel_fclk_div3 (3 << 23)
#define spicc1_clk_en (1 << 22)
#define spicc1_clk_div(x) (((x) - 1) << 16)
namespace {
constexpr char kSpi1SchedulerRole[] = "fuchsia.devices.spi.drivers.aml-spi.spi1";
} // namespace
namespace fdf {
using namespace fuchsia_driver_framework;
} // namespace fdf
namespace nelson {
namespace fpbus = fuchsia_hardware_platform_bus;
using spi_channel_t = fidl_metadata::spi::Channel;
fdf::wire::CompositeNodeSpec MakeSpiCompositeNodeSpec(fidl::AnyArena& fidl_arena, std::string name,
uint32_t gpio_pin, std::string gpio_function,
std::string register_id) {
const std::vector kGpioSpiRules = {
fdf::MakeAcceptBindRule(bind_fuchsia_hardware_gpio::SERVICE,
bind_fuchsia_hardware_gpio::SERVICE_ZIRCONTRANSPORT),
fdf::MakeAcceptBindRule(bind_fuchsia::GPIO_PIN, gpio_pin),
};
const std::vector kGpioSpiProperties = {
fdf::MakeProperty(bind_fuchsia_hardware_gpio::SERVICE,
bind_fuchsia_hardware_gpio::SERVICE_ZIRCONTRANSPORT),
fdf::MakeProperty(bind_fuchsia_gpio::FUNCTION, gpio_function),
};
const std::vector kResetRegisterRules = {
fdf::MakeAcceptBindRule(bind_fuchsia_hardware_registers::SERVICE,
bind_fuchsia_hardware_registers::SERVICE_ZIRCONTRANSPORT),
fdf::MakeAcceptBindRule(bind_fuchsia_register::NAME, register_id),
};
const std::vector kResetRegisterProperties = {
fdf::MakeProperty(bind_fuchsia_hardware_registers::SERVICE,
bind_fuchsia_hardware_registers::SERVICE_ZIRCONTRANSPORT),
fdf::MakeProperty(bind_fuchsia_register::NAME, register_id),
};
const std::vector<fdf::BindRule> kGpioInitRules = std::vector{
fdf::MakeAcceptBindRule(bind_fuchsia::INIT_STEP, bind_fuchsia_gpio::BIND_INIT_STEP_GPIO),
};
const std::vector<fdf::NodeProperty> kGpioInitProperties = std::vector{
fdf::MakeProperty(bind_fuchsia::INIT_STEP, bind_fuchsia_gpio::BIND_INIT_STEP_GPIO),
};
const std::vector<fdf::ParentSpec> parents = {
{kGpioSpiRules, kGpioSpiProperties},
{kResetRegisterRules, kResetRegisterProperties},
{kGpioInitRules, kGpioInitProperties},
};
return fidl::ToWire(fidl_arena, fdf::CompositeNodeSpec{{.name = name, .parents = parents}});
}
zx_status_t Nelson::SpiInit() {
constexpr uint32_t kSpiccClkValue =
// SPICC0 clock enable (500 MHz)
spicc0_clk_sel_fclk_div4 | spicc0_clk_en | spicc0_clk_div(1) |
// SPICC1 clock enable (666 MHz)
spicc1_clk_sel_fclk_div3 | spicc1_clk_en | spicc1_clk_div(1);
// TODO(https://fxbug.dev/42109271): fix this clock enable block when the clock driver can handle
// the dividers
{
zx::unowned_resource resource(get_mmio_resource(parent()));
zx::vmo vmo;
zx_status_t status =
zx::vmo::create_physical(*resource, S905D3_HIU_BASE, S905D3_HIU_LENGTH, &vmo);
if (status != ZX_OK) {
zxlogf(ERROR, "failed to create VMO: %s", zx_status_get_string(status));
return status;
}
zx::result<fdf::MmioBuffer> buf = fdf::MmioBuffer::Create(0, S905D3_HIU_LENGTH, std::move(vmo),
ZX_CACHE_POLICY_UNCACHED_DEVICE);
if (buf.is_error()) {
zxlogf(ERROR, "fdf::MmioBuffer::Create() error: %s", buf.status_string());
return buf.status_value();
}
buf->Write32(kSpiccClkValue, HHI_SPICC_CLK_CNTL);
}
zx_status_t status0 = Spi0Init();
zx_status_t status1 = Spi1Init();
return status0 == ZX_OK ? status1 : status0;
}
zx_status_t Nelson::Spi0Init() {
static const std::vector<fpbus::Mmio> spi_0_mmios{
{{
.base = S905D3_SPICC0_BASE,
.length = S905D3_SPICC0_LENGTH,
}},
};
static const std::vector<fpbus::Irq> spi_0_irqs{
{{
.irq = S905D3_SPICC0_IRQ,
.mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
}},
};
static const spi_channel_t spi_0_channels[] = {
{
.cs = 0, // index into matching chip-select map
.vid = PDEV_VID_NORDIC,
.pid = PDEV_PID_NORDIC_NRF52811,
.did = PDEV_DID_NORDIC_THREAD,
},
};
static const amlogic_spi::amlspi_config_t spi_0_config = {
.bus_id = NELSON_SPICC0,
.cs_count = 1,
.cs = {0}, // index into fragments list
.clock_divider_register_value = (500 >> 1) - 1, // SCLK = core clock / 500 = 1.0 MHz
.use_enhanced_clock_mode = true,
};
fpbus::Node spi_0_dev;
spi_0_dev.name() = "spi-0";
spi_0_dev.vid() = bind_fuchsia_amlogic_platform::BIND_PLATFORM_DEV_VID_AMLOGIC;
spi_0_dev.pid() = bind_fuchsia_platform::BIND_PLATFORM_DEV_PID_GENERIC;
spi_0_dev.did() = bind_fuchsia_amlogic_platform::BIND_PLATFORM_DEV_DID_SPI;
spi_0_dev.instance_id() = 0;
spi_0_dev.mmio() = spi_0_mmios;
spi_0_dev.irq() = spi_0_irqs;
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_MOSI, GpioSetAltFunction(5)}); // MOSI
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_MOSI, GpioSetDriveStrength(2500)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_MISO, GpioSetAltFunction(5)}); // MISO
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_MISO, GpioSetDriveStrength(2500)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_SS0, GpioSetAltFunction(0)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_SS0, GpioConfigOut(1)}); // SS0
// SCLK must be pulled down to prevent SPI bit errors.
gpio_init_steps_.push_back(
{GPIO_SOC_SPI_A_SCLK, GpioConfigIn(fuchsia_hardware_gpio::GpioFlags::kPullDown)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_SCLK, GpioSetAltFunction(5)}); // SCLK
gpio_init_steps_.push_back({GPIO_SOC_SPI_A_SCLK, GpioSetDriveStrength(2500)});
std::vector<fpbus::Metadata> spi_0_metadata;
spi_0_metadata.emplace_back([]() {
fpbus::Metadata ret;
ret.type() = DEVICE_METADATA_AMLSPI_CONFIG;
ret.data() = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&spi_0_config),
reinterpret_cast<const uint8_t*>(&spi_0_config) + sizeof(spi_0_config));
return ret;
}());
auto spi_status = fidl_metadata::spi::SpiChannelsToFidl(NELSON_SPICC0, spi_0_channels);
if (spi_status.is_error()) {
zxlogf(ERROR, "%s: failed to encode spi channels to fidl: %d", __func__,
spi_status.error_value());
return spi_status.error_value();
}
auto& data = spi_status.value();
spi_0_metadata.emplace_back([&]() {
fpbus::Metadata ret;
ret.type() = DEVICE_METADATA_SPI_CHANNELS;
ret.data() = std::move(data);
return ret;
}());
spi_0_dev.metadata() = std::move(spi_0_metadata);
fidl::Arena<> fidl_arena;
fdf::Arena arena('SPI0');
auto result = pbus_.buffer(arena)->AddCompositeNodeSpec(
fidl::ToWire(fidl_arena, spi_0_dev),
MakeSpiCompositeNodeSpec(
fidl_arena, "spi_0", /* gpio_pin */ GPIO_SOC_SPI_A_SS0,
/* gpio_function */ bind_fuchsia_gpio::FUNCTION_SPICC0_SS0,
/* register_id */ bind_fuchsia_amlogic_platform::NAME_REGISTER_SPICC0_RESET));
if (!result.ok()) {
zxlogf(ERROR, "AddCompositeNodeSpec Spi0(spi_0_dev) request failed: %s",
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "AddCompositeNodeSpec Spi0(spi_0_dev) failed: %s",
zx_status_get_string(result->error_value()));
return result->error_value();
}
return ZX_OK;
}
zx_status_t Nelson::Spi1Init() {
static const std::vector<fpbus::Mmio> spi_1_mmios{
{{
.base = S905D3_SPICC1_BASE,
.length = S905D3_SPICC1_LENGTH,
}},
};
static const std::vector<fpbus::Irq> spi_1_irqs{
{{
.irq = S905D3_SPICC1_IRQ,
.mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
}},
};
static const std::vector<fpbus::Bti> spi_1_btis{
{{
.iommu_index = 0,
.bti_id = BTI_SPI1,
}},
};
static const spi_channel_t spi_1_channels[] = {
// Radar sensor head.
{
.cs = 0, // index into matching chip-select map
.vid = PDEV_VID_INFINEON,
.pid = PDEV_PID_INFINEON_BGT60TR13C,
.did = PDEV_DID_RADAR_SENSOR,
},
};
constexpr uint32_t kMoNoDelay = 0 << 0;
constexpr uint32_t kMiDelay3Cycles = 3 << 2;
constexpr uint32_t kMiCapAhead2Cycles = 0 << 4;
static const amlogic_spi::amlspi_config_t spi_1_config = {
.bus_id = NELSON_SPICC1,
.cs_count = 1,
.cs = {amlogic_spi::amlspi_config_t::kCsClientManaged}, // CS GPIO managed by client driver
.clock_divider_register_value = (22 >> 1) - 1, // SCLK = core clock / 22 = 30.3 MHz
.use_enhanced_clock_mode = true,
.client_reverses_dma_transfers = true,
.delay_control = kMoNoDelay | kMiDelay3Cycles | kMiCapAhead2Cycles,
};
fpbus::Node spi_1_dev;
spi_1_dev.name() = "spi-1";
spi_1_dev.vid() = bind_fuchsia_amlogic_platform::BIND_PLATFORM_DEV_VID_AMLOGIC;
spi_1_dev.pid() = bind_fuchsia_platform::BIND_PLATFORM_DEV_PID_GENERIC;
spi_1_dev.did() = bind_fuchsia_amlogic_platform::BIND_PLATFORM_DEV_DID_SPI;
spi_1_dev.instance_id() = 1;
spi_1_dev.mmio() = spi_1_mmios;
spi_1_dev.irq() = spi_1_irqs;
spi_1_dev.bti() = spi_1_btis;
// setup pinmux for SPICC1 bus arbiter.
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_MOSI, GpioSetAltFunction(3)}); // MOSI
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_MOSI, GpioSetDriveStrength(2500)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_MISO, GpioSetAltFunction(3)}); // MISO
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_MISO, GpioSetDriveStrength(2500)});
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_SS0, GpioConfigOut(1)}); // SS0
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_SCLK, GpioSetAltFunction(3)}); // SCLK
gpio_init_steps_.push_back({GPIO_SOC_SPI_B_SCLK, GpioSetDriveStrength(2500)});
std::vector<fpbus::Metadata> spi_1_metadata;
spi_1_metadata.emplace_back([]() {
fpbus::Metadata ret;
ret.type() = DEVICE_METADATA_AMLSPI_CONFIG,
ret.data() = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&spi_1_config),
reinterpret_cast<const uint8_t*>(&spi_1_config) + sizeof(spi_1_config));
return ret;
}());
{
const fuchsia_scheduler::RoleName role(kSpi1SchedulerRole);
fit::result result = fidl::Persist(role);
if (result.is_error()) {
zxlogf(ERROR, "Failed to persist scheduler role: %s",
result.error_value().FormatDescription().c_str());
return result.error_value().status();
}
spi_1_metadata.emplace_back(
fpbus::Metadata{{DEVICE_METADATA_SCHEDULER_ROLE_NAME, *std::move(result)}});
}
auto spi_status = fidl_metadata::spi::SpiChannelsToFidl(NELSON_SPICC1, spi_1_channels);
if (spi_status.is_error()) {
zxlogf(ERROR, "%s: failed to encode spi channels to fidl: %d", __func__,
spi_status.error_value());
return spi_status.error_value();
}
auto& data = spi_status.value();
spi_1_metadata.emplace_back([&]() {
fpbus::Metadata ret;
ret.type() = DEVICE_METADATA_SPI_CHANNELS;
ret.data() = std::move(data);
return ret;
}());
spi_1_dev.metadata() = std::move(spi_1_metadata);
fdf::Arena arena('SPI1');
fidl::Arena<> fidl_arena;
auto result = pbus_.buffer(arena)->AddCompositeNodeSpec(
fidl::ToWire(fidl_arena, spi_1_dev),
MakeSpiCompositeNodeSpec(
fidl_arena, "spi_1", /* gpio_pin */ GPIO_SOC_SPI_B_SS0,
/* gpio_function */ bind_fuchsia_gpio::FUNCTION_SPICC1_SS0,
/* register_id */ bind_fuchsia_amlogic_platform::NAME_REGISTER_SPICC1_RESET));
if (!result.ok()) {
zxlogf(ERROR, "AddCompositeNodeSpec Spi1(spi_1_dev) request failed: %s",
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "AddCompositeNodeSpec Spi1(spi_1_dev) failed: %s",
zx_status_get_string(result->error_value()));
return result->error_value();
}
return ZX_OK;
}
} // namespace nelson