blob: 2b96daa40f37bf19fedefd47c6c4f0c567984c8d [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 "as370-gpio.h"
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/platform-defs.h>
#include <ddktl/protocol/platform/bus.h>
#include <fbl/alloc_checker.h>
#include <fbl/unique_ptr.h>
#include <lib/device-protocol/pdev.h>
namespace {
constexpr zx_off_t kGpioSwPortADr = 0x00;
constexpr zx_off_t kGpioSwPortADdr = 0x04;
constexpr zx_off_t kGpioExtPortA = 0x50;
constexpr zx_off_t kPinmuxCntlBusBase = 0x40;
constexpr uint32_t kPorts = 2;
constexpr uint32_t kGpiosPerPort = 32;
constexpr uint32_t kTotalPins = 72;
constexpr uint32_t kPinmuxFunctionWidth = 3;
constexpr uint32_t kPinmuxPinsPerReg = 10;
constexpr uint32_t kGpioPinmuxWindowOffset = 18;
uint32_t GetGpioBitOffset(uint32_t index) {
return (index < kGpiosPerPort) ? index : (index - kGpiosPerPort);
}
} // namespace
namespace gpio {
zx_status_t As370Gpio::Create(void* ctx, zx_device_t* parent) {
ddk::PDev pdev(parent);
if (!pdev.is_valid()) {
zxlogf(ERROR, "%s: Failed to get ZX_PROTOCOL_PLATFORM_DEVICE\n", __FILE__);
return ZX_ERR_NO_RESOURCES;
}
std::optional<ddk::MmioBuffer> pinctrl_mmio;
zx_status_t status = pdev.MapMmio(0, &pinctrl_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to map pinmux MMIO: %d\n", __FILE__, status);
return status;
}
std::optional<ddk::MmioBuffer> gpio1_mmio;
if ((status = pdev.MapMmio(1, &gpio1_mmio)) != ZX_OK) {
zxlogf(ERROR, "%s: Failed to map GPIO 1 MMIO: %d\n", __FILE__, status);
return status;
}
std::optional<ddk::MmioBuffer> gpio2_mmio;
if ((status = pdev.MapMmio(2, &gpio2_mmio)) != ZX_OK) {
zxlogf(ERROR, "%s: Failed to map GPIO 2 MMIO: %d\n", __FILE__, status);
return status;
}
fbl::AllocChecker ac;
auto device = fbl::make_unique_checked<As370Gpio>(&ac, parent, *std::move(pinctrl_mmio),
*std::move(gpio1_mmio), *std::move(gpio2_mmio));
if (!ac.check()) {
zxlogf(ERROR, "%s: Failed to allocate device memory\n", __FILE__);
return ZX_ERR_NO_MEMORY;
}
if ((status = device->DdkAdd("as370-gpio")) != ZX_OK) {
zxlogf(ERROR, "%s: Bind failed: %d\n", __FILE__, status);
return status;
}
status = device->Init();
__UNUSED auto* dummy = device.release();
return status;
}
zx_status_t As370Gpio::Init() {
ddk::PBusProtocolClient pbus(parent());
if (!pbus.is_valid()) {
zxlogf(ERROR, "%s: Failed to get ZX_PROTOCOL_PLATFORM_BUS\n", __FILE__);
return ZX_ERR_NO_RESOURCES;
}
gpio_impl_protocol_t gpio_proto = {.ops = &gpio_impl_protocol_ops_, .ctx = this};
zx_status_t status = pbus.RegisterProtocol(ddk_proto_id_, &gpio_proto, sizeof(gpio_proto));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to register ZX_PROTOCOL_GPIO_IMPL: %d\n", __FILE__, __LINE__);
return status;
}
return ZX_OK;
}
zx_status_t As370Gpio::GpioImplConfigIn(uint32_t index, uint32_t flags) {
if (index >= kPorts * kGpiosPerPort) {
return ZX_ERR_OUT_OF_RANGE;
} else if (flags != GPIO_NO_PULL) {
// TODO(bradenkell): Add support for enabling pull up/down resistors.
return ZX_ERR_NOT_SUPPORTED;
}
const ddk::MmioBuffer& gpio_mmio = (index < kGpiosPerPort) ? gpio1_mmio_ : gpio2_mmio_;
gpio_mmio.ClearBit<uint32_t>(GetGpioBitOffset(index), kGpioSwPortADdr);
return ZX_OK;
}
zx_status_t As370Gpio::GpioImplConfigOut(uint32_t index, uint8_t initial_value) {
if (index >= kPorts * kGpiosPerPort) {
return ZX_ERR_OUT_OF_RANGE;
}
GpioImplWrite(index, initial_value);
const ddk::MmioBuffer& gpio_mmio = (index < kGpiosPerPort) ? gpio1_mmio_ : gpio2_mmio_;
gpio_mmio.SetBit<uint32_t>(GetGpioBitOffset(index), kGpioSwPortADdr);
return ZX_OK;
}
zx_status_t As370Gpio::GpioImplSetAltFunction(uint32_t index, uint64_t function) {
// The pinmux registers have a gap with respect to the GPIOs, like this:
// |----- GPIOs 0-17 -----|--- NAND pins ---|--------------- GPIOs 18-63 ---------------|
// The NAND pins are mapped to GPIOs 63-71, so the index parameter must be adjusted accordingly.
if (index >= kTotalPins || function > ((1 << kPinmuxFunctionWidth) - 1)) {
return ZX_ERR_OUT_OF_RANGE;
} else if (index >= kPorts * kGpiosPerPort) {
index -= (kPorts * kGpiosPerPort) - kGpioPinmuxWindowOffset;
} else if (index >= kGpioPinmuxWindowOffset) {
index += kTotalPins - (kPorts * kGpiosPerPort);
}
zx_off_t reg_offset = (sizeof(uint32_t) * (index / kPinmuxPinsPerReg)) + kPinmuxCntlBusBase;
uint32_t bit_offset = (index % kPinmuxPinsPerReg) * kPinmuxFunctionWidth;
pinmux_mmio_.ModifyBits<uint32_t>(static_cast<uint32_t>(function), bit_offset,
kPinmuxFunctionWidth, reg_offset);
return ZX_OK;
}
zx_status_t As370Gpio::GpioImplSetDriveStrength(uint32_t index, uint8_t m_a) {
// TODO(bradenkell): Implement this.
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t As370Gpio::GpioImplRead(uint32_t index, uint8_t* out_value) {
if (index >= kPorts * kGpiosPerPort) {
return ZX_ERR_OUT_OF_RANGE;
}
const ddk::MmioBuffer& gpio_mmio = (index < kGpiosPerPort) ? gpio1_mmio_ : gpio2_mmio_;
const uint32_t mask = 1 << GetGpioBitOffset(index);
*out_value = ((gpio_mmio.Read32(kGpioExtPortA) & mask) == 0) ? 0 : 1;
return ZX_OK;
}
zx_status_t As370Gpio::GpioImplWrite(uint32_t index, uint8_t value) {
if (index >= kPorts * kGpiosPerPort) {
return ZX_ERR_OUT_OF_RANGE;
}
const ddk::MmioBuffer& gpio_mmio = (index < kGpiosPerPort) ? gpio1_mmio_ : gpio2_mmio_;
gpio_mmio.ModifyBit<uint32_t>(value, GetGpioBitOffset(index), kGpioSwPortADr);
return ZX_OK;
}
// TODO(bradenkell): Implement these.
zx_status_t As370Gpio::GpioImplGetInterrupt(uint32_t index, uint32_t flags,
zx::interrupt* out_irq) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t As370Gpio::GpioImplReleaseInterrupt(uint32_t index) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t As370Gpio::GpioImplSetPolarity(uint32_t index, gpio_polarity_t polarity) {
return ZX_ERR_NOT_SUPPORTED;
}
} // namespace gpio
static constexpr zx_driver_ops_t as370_gpio_driver_ops = []() -> zx_driver_ops_t {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = gpio::As370Gpio::Create;
return ops;
}();
ZIRCON_DRIVER_BEGIN(as370_gpio, as370_gpio_driver_ops, "zircon", "0.1", 2)
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_SYNAPTICS),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_SYNAPTICS_GPIO), ZIRCON_DRIVER_END(as370_gpio)