blob: 163228c187764f60549dd48113e3f5847b6cbb31 [file] [log] [blame]
// Copyright 2018 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/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/metadata.h>
#include <lib/ddk/platform-defs.h>
#include <ddk/metadata/gpio.h>
#include <soc/aml-s905d3/s905d3-gpio.h>
#include <soc/aml-s905d3/s905d3-hw.h>
#include "nelson-gpios.h"
#include "nelson.h"
// uncomment to disable LED blinky test
// #define GPIO_TEST
namespace {
constexpr char kGpioHSchedulerRole[] = "fuchsia.devices.gpio.drivers.aml-gpio.gpioh";
} // namespace
namespace nelson {
namespace fpbus = fuchsia_hardware_platform_bus;
static const std::vector<fpbus::Mmio> gpio_mmios{
{{
.base = S905D3_GPIO_BASE,
.length = S905D3_GPIO_LENGTH,
}},
{{
.base = S905D3_GPIO_AO_BASE,
.length = S905D3_GPIO_AO_LENGTH,
}},
{{
.base = S905D3_GPIO_INTERRUPT_BASE,
.length = S905D3_GPIO_INTERRUPT_LENGTH,
}},
};
static const std::vector<fpbus::Irq> gpio_irqs{
{{
.irq = S905D3_GPIO_IRQ_0,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_1,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_2,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_3,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_4,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_5,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_6,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
{{
.irq = S905D3_GPIO_IRQ_7,
.mode = ZX_INTERRUPT_MODE_DEFAULT,
}},
};
// GPIOs to expose from generic GPIO driver. Do not expose C bank or H bank GPIOs here, as they are
// managed by separate devices below. The three GPIO devices are not capable of synchronizing
// accesses to the interrupt registers, so C and H bank GPIOs that are used for interrupts must be
// exposed by the main device (only GPIO_SOC_SELINA_IRQ_OUT and GPIO_TH_SOC_INT). All pins can be
// used in calls from the board driver, regardless of bank.
static const gpio_pin_t gpio_pins[] = {
DECL_GPIO_PIN(GPIO_INRUSH_EN_SOC),
DECL_GPIO_PIN(GPIO_SOC_I2S_SCLK),
DECL_GPIO_PIN(GPIO_SOC_I2S_FS),
DECL_GPIO_PIN(GPIO_SOC_I2S_DO0),
DECL_GPIO_PIN(GPIO_SOC_I2S_DIN0),
DECL_GPIO_PIN(GPIO_SOC_AUDIO_EN),
DECL_GPIO_PIN(GPIO_SOC_MIC_DCLK),
DECL_GPIO_PIN(GPIO_SOC_MICLR_DIN0),
DECL_GPIO_PIN(GPIO_SOC_MICLR_DIN1),
DECL_GPIO_PIN(GPIO_SOC_BKL_EN),
DECL_GPIO_PIN(GPIO_AUDIO_SOC_FAULT_L),
DECL_GPIO_PIN(GPIO_SOC_TH_RST_L),
DECL_GPIO_PIN(GPIO_SOC_AV_I2C_SDA),
DECL_GPIO_PIN(GPIO_SOC_AV_I2C_SCL),
DECL_GPIO_PIN(GPIO_HW_ID_3),
DECL_GPIO_PIN(GPIO_SOC_TH_BOOT_MODE_L),
DECL_GPIO_PIN(GPIO_MUTE_SOC),
DECL_GPIO_PIN(GPIO_HW_ID_2),
DECL_GPIO_PIN(GPIO_TOUCH_SOC_INT_L),
DECL_GPIO_PIN(GPIO_VOL_UP_L),
DECL_GPIO_PIN(GPIO_VOL_DN_L),
DECL_GPIO_PIN(GPIO_HW_ID_0),
DECL_GPIO_PIN(GPIO_HW_ID_1),
DECL_GPIO_PIN(GPIO_SOC_TOUCH_RST_L),
DECL_GPIO_PIN(GPIO_ALERT_PWR_L),
DECL_GPIO_PIN(GPIO_DISP_SOC_ID0),
DECL_GPIO_PIN(GPIO_DISP_SOC_ID1),
DECL_GPIO_PIN(GPIO_SOC_DISP_RST_L),
DECL_GPIO_PIN(GPIO_SOC_TOUCH_I2C_SDA),
DECL_GPIO_PIN(GPIO_SOC_TOUCH_I2C_SCL),
// ot-radio is responsible for not making concurrent calls to this GPIO and the GPIO C device
// (or other clients of that device, namely SPI0). Calls may be made on the interrupt object
// (and interrupts may be received) at any time, as there is no GPIO driver involvement in that
// case.
DECL_GPIO_PIN(GPIO_TH_SOC_INT),
DECL_GPIO_PIN(GPIO_SOC_TH_INT),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_D0),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_D1),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_D2),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_D3),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_CLK),
DECL_GPIO_PIN(GPIO_SOC_WIFI_SDIO_CMD),
DECL_GPIO_PIN(GPIO_SOC_WIFI_REG_ON),
DECL_GPIO_PIN(GPIO_WIFI_SOC_WAKE),
DECL_GPIO_PIN(GPIO_SOC_BT_PCM_IN),
DECL_GPIO_PIN(GPIO_SOC_BT_PCM_OUT),
DECL_GPIO_PIN(GPIO_SOC_BT_PCM_SYNC),
DECL_GPIO_PIN(GPIO_SOC_BT_PCM_CLK),
DECL_GPIO_PIN(GPIO_SOC_BT_UART_TX),
DECL_GPIO_PIN(GPIO_SOC_BT_UART_RX),
DECL_GPIO_PIN(GPIO_SOC_BT_UART_CTS),
DECL_GPIO_PIN(GPIO_SOC_BT_UART_RTS),
DECL_GPIO_PIN(GPIO_SOC_WIFI_LPO_32K768),
DECL_GPIO_PIN(GPIO_SOC_BT_REG_ON),
DECL_GPIO_PIN(GPIO_BT_SOC_WAKE),
DECL_GPIO_PIN(GPIO_SOC_BT_WAKE),
// Like above -- Selina is responsible for not making current calls to this GPIO and the GPIO H
// device (or SPI1, which is a client of the GPIO H device).
DECL_GPIO_PIN(GPIO_SOC_SELINA_IRQ_OUT),
DECL_GPIO_PIN(GPIO_SOC_DEBUG_UARTAO_TX),
DECL_GPIO_PIN(GPIO_SOC_DEBUG_UARTAO_RX),
DECL_GPIO_PIN(GPIO_SOC_SENSORS_I2C_SCL),
DECL_GPIO_PIN(GPIO_SOC_SENSORS_I2C_SDA),
DECL_GPIO_PIN(GPIO_HW_ID_4),
DECL_GPIO_PIN(GPIO_RGB_SOC_INT_L),
DECL_GPIO_PIN(GPIO_SOC_JTAG_TCK),
DECL_GPIO_PIN(GPIO_SOC_JTAG_TMS),
DECL_GPIO_PIN(GPIO_SOC_JTAG_TDI),
DECL_GPIO_PIN(GPIO_SOC_JTAG_TDO),
DECL_GPIO_PIN(GPIO_FDR_L),
DECL_GPIO_PIN(GPIO_AMBER_LED_PWM),
DECL_GPIO_PIN(GPIO_SOC_VDDEE_PWM),
DECL_GPIO_PIN(GPIO_SOC_VDDCPU_PWM),
DECL_GPIO_PIN(SOC_EMMC_D0),
DECL_GPIO_PIN(SOC_EMMC_D1),
DECL_GPIO_PIN(SOC_EMMC_D2),
DECL_GPIO_PIN(SOC_EMMC_D3),
DECL_GPIO_PIN(SOC_EMMC_D4),
DECL_GPIO_PIN(SOC_EMMC_D5),
DECL_GPIO_PIN(SOC_EMMC_D6),
DECL_GPIO_PIN(SOC_EMMC_D7),
DECL_GPIO_PIN(SOC_EMMC_CLK),
DECL_GPIO_PIN(SOC_EMMC_CMD),
DECL_GPIO_PIN(SOC_EMMC_RST_L),
DECL_GPIO_PIN(SOC_EMMC_DS),
};
// The GPIO H device won't be able to provide interrupts for the pins it exposes, so
// GPIO_SOC_SELINA_IRQ_OUT must be be exposed by the main GPIO device (see the list of pins above)
// instead of this one.
static const gpio_pin_t gpio_h_pins[] = {
DECL_GPIO_PIN(GPIO_SOC_SELINA_RESET), DECL_GPIO_PIN(GPIO_SOC_SPI_B_MOSI),
DECL_GPIO_PIN(GPIO_SOC_SPI_B_MISO), DECL_GPIO_PIN(GPIO_SOC_SPI_B_SS0),
DECL_GPIO_PIN(GPIO_SOC_SPI_B_SCLK), DECL_GPIO_PIN(GPIO_SOC_SELINA_OSC_EN)};
static const gpio_pin_t gpio_c_pins[] = {
DECL_GPIO_PIN(GPIO_SOC_SPI_A_MISO),
DECL_GPIO_PIN(GPIO_SOC_SPI_A_MOSI),
DECL_GPIO_PIN(GPIO_SOC_SPI_A_SCLK),
DECL_GPIO_PIN(GPIO_SOC_SPI_A_SS0),
};
static const std::vector<fpbus::Metadata> gpio_c_metadata{
{{
.type = DEVICE_METADATA_GPIO_PINS,
.data = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&gpio_c_pins),
reinterpret_cast<const uint8_t*>(&gpio_c_pins) + sizeof(gpio_c_pins)),
}},
};
static const fpbus::Node gpio_c_dev = []() {
fpbus::Node dev = {};
dev.name() = "gpio-c";
dev.vid() = PDEV_VID_AMLOGIC;
dev.pid() = PDEV_PID_AMLOGIC_S905D3;
dev.did() = PDEV_DID_AMLOGIC_GPIO;
dev.instance_id() = 2;
dev.mmio() = gpio_mmios;
dev.metadata() = gpio_c_metadata;
return dev;
}();
zx_status_t Nelson::GpioInit() {
// Enable mute LED so it will be controlled by mute switch.
gpio_init_steps_.push_back({GPIO_AMBER_LED_PWM, GpioConfigOut(1)});
fuchsia_hardware_gpioimpl::wire::InitMetadata metadata;
metadata.steps = fidl::VectorView<fuchsia_hardware_gpioimpl::wire::InitStep>::FromExternal(
gpio_init_steps_.data(), gpio_init_steps_.size());
const fit::result encoded_metadata = fidl::Persist(metadata);
if (!encoded_metadata.is_ok()) {
zxlogf(ERROR, "Failed to encode GPIO init metadata: %s",
encoded_metadata.error_value().FormatDescription().c_str());
return encoded_metadata.error_value().status();
}
const std::vector<fpbus::Metadata> gpio_metadata{
{{
.type = DEVICE_METADATA_GPIO_PINS,
.data = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&gpio_pins),
reinterpret_cast<const uint8_t*>(&gpio_pins) + sizeof(gpio_pins)),
}},
{{
.type = DEVICE_METADATA_GPIO_INIT,
.data = encoded_metadata.value(),
}},
};
fpbus::Node gpio_dev;
gpio_dev.name() = "gpio";
gpio_dev.vid() = PDEV_VID_AMLOGIC;
gpio_dev.pid() = PDEV_PID_AMLOGIC_S905D3;
gpio_dev.did() = PDEV_DID_AMLOGIC_GPIO;
gpio_dev.mmio() = gpio_mmios;
gpio_dev.irq() = gpio_irqs;
gpio_dev.metadata() = gpio_metadata;
fidl::Arena<> fidl_arena;
fdf::Arena arena('GPIO');
auto result = pbus_.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, gpio_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_dev) request failed: %s", __func__,
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_dev) failed: %s", __func__,
zx_status_get_string(result->error_value()));
return result->error_value();
}
const fuchsia_scheduler::RoleName role(kGpioHSchedulerRole);
fit::result role_metadata = fidl::Persist(role);
if (role_metadata.is_error()) {
zxlogf(ERROR, "Failed to persist scheduler role: %s",
role_metadata.error_value().FormatDescription().c_str());
return role_metadata.error_value().status();
}
const std::vector<fpbus::Metadata> gpio_h_metadata{
{{
.type = DEVICE_METADATA_GPIO_PINS,
.data = std::vector<uint8_t>(
reinterpret_cast<const uint8_t*>(&gpio_h_pins),
reinterpret_cast<const uint8_t*>(&gpio_h_pins) + sizeof(gpio_h_pins)),
}},
{{
.type = DEVICE_METADATA_SCHEDULER_ROLE_NAME,
.data = role_metadata.value(),
}},
};
const fpbus::Node gpio_h_dev = [&gpio_h_metadata]() {
fpbus::Node dev = {};
dev.name() = "gpio-h";
dev.vid() = PDEV_VID_AMLOGIC;
dev.pid() = PDEV_PID_AMLOGIC_S905D3;
dev.did() = PDEV_DID_AMLOGIC_GPIO;
dev.instance_id() = 1;
dev.mmio() = gpio_mmios;
dev.metadata() = gpio_h_metadata;
return dev;
}();
{
auto result = pbus_.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, gpio_h_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_h_dev) request failed: %s", __func__,
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_h_dev) failed: %s", __func__,
zx_status_get_string(result->error_value()));
return result->error_value();
}
}
{
auto result = pbus_.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, gpio_c_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_c_dev) request failed: %s", __func__,
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_c_dev) failed: %s", __func__,
zx_status_get_string(result->error_value()));
return result->error_value();
}
}
#ifdef GPIO_TEST
static const std::vector<fpbus::Gpio> gpio_test_gpios{
{{
// SYS_LED
.gpio = GPIO_AMBER_LED_PWM,
}},
{{
// JTAG Adapter Pin
.gpio = GPIO_SOC_JTAG_TCK,
}},
};
fpbus::Node gpio_test_dev;
gpio_test_dev.name() = "nelson-gpio-test";
gpio_test_dev.vid() = PDEV_VID_GENERIC;
gpio_test_dev.pid() = PDEV_PID_GENERIC;
gpio_test_dev.did() = PDEV_DID_GPIO_TEST;
gpio_test_dev.gpio() = gpio_test_gpios;
result = pbus_.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, gpio_test_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_test_dev) request failed: %s", __func__,
result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd Gpio(gpio_test_dev) failed: %s", __func__,
zx_status_get_string(result->error_value()));
return result->error_value();
}
#endif
return ZX_OK;
}
} // namespace nelson