blob: 4efe26aed1e5e493fbac19f97b84a410427a5bce [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.
#ifndef SRC_DEVICES_GPIO_DRIVERS_AML_GXL_GPIO_AML_GXL_GPIO_H_
#define SRC_DEVICES_GPIO_DRIVERS_AML_GXL_GPIO_AML_GXL_GPIO_H_
#include <inttypes.h>
#include <lib/mmio/mmio.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <utility>
#include <ddk/protocol/platform/bus.h>
#include <ddktl/device.h>
#include <ddktl/protocol/gpioimpl.h>
#include <ddktl/protocol/platform/device.h>
#include <fbl/array.h>
#include <fbl/mutex.h>
#include <fbl/vector.h>
#include <hw/reg.h>
namespace gpio {
constexpr int kPinsPerBlock = 32;
constexpr uint64_t kAltFunctionMax = 6;
struct AmlGpioBlock {
uint32_t pin_count;
uint32_t oen_offset;
uint32_t input_offset;
uint32_t output_offset;
uint32_t output_shift; // Used for GPIOAO block
uint32_t output_write_shift = 0;
uint32_t mmio_index;
uint32_t pull_offset;
uint32_t pull_en_offset;
uint32_t pin_start;
};
struct AmlPinMux {
// pinmux register offsets for the alternate functions.
// zero means alternate function not supported.
uint8_t regs[kAltFunctionMax];
// bit number to set/clear to enable/disable alternate function
uint8_t bits[kAltFunctionMax];
};
struct AmlPinMuxBlock {
AmlPinMux mux[kPinsPerBlock];
};
struct AmlGpioInterrupt {
uint32_t pin_select_offset;
uint32_t edge_polarity_offset;
uint32_t filter_select_offset;
uint32_t status_offset;
uint32_t mask_offset;
};
class AmlGxlGpio;
using DeviceType = ddk::Device<AmlGxlGpio, ddk::Unbindable>;
class AmlGxlGpio : public DeviceType, public ddk::GpioImplProtocol<AmlGxlGpio, ddk::base_protocol> {
public:
static zx_status_t Create(zx_device_t* parent);
zx_status_t GpioImplConfigIn(uint32_t index, uint32_t flags);
zx_status_t GpioImplConfigOut(uint32_t index, uint8_t initial_value);
zx_status_t GpioImplSetAltFunction(uint32_t pin, uint64_t function);
zx_status_t GpioImplRead(uint32_t pin, uint8_t* out_value);
zx_status_t GpioImplWrite(uint32_t pin, uint8_t value);
zx_status_t GpioImplGetInterrupt(uint32_t pin, uint32_t flags, zx::interrupt* out_irq);
zx_status_t GpioImplReleaseInterrupt(uint32_t pin);
zx_status_t GpioImplSetPolarity(uint32_t pin, uint32_t polarity);
zx_status_t GpioImplSetDriveStrength(uint32_t index, uint64_t ua, uint64_t* out_actual_ua) {
return ZX_ERR_NOT_SUPPORTED;
}
void DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
void DdkRelease() { delete this; }
private:
AmlGxlGpio(zx_device_t* parent, const pdev_protocol_t& pdev, ddk::MmioBuffer mmio_gpio,
ddk::MmioBuffer mmio_gpio_a0, ddk::MmioBuffer mmio_interrupt,
const AmlGpioBlock* gpio_blocks, const AmlGpioInterrupt* gpio_interrupt,
const AmlPinMuxBlock* pinmux_blocks, size_t block_count, fbl::Array<uint16_t> irq_info)
: DeviceType(parent),
pdev_(pdev),
mmios_{std::move(mmio_gpio), std::move(mmio_gpio_a0)},
mmio_interrupt_(std::move(mmio_interrupt)),
gpio_blocks_(gpio_blocks),
gpio_interrupt_(gpio_interrupt),
pinmux_blocks_(pinmux_blocks),
block_count_(block_count),
irq_info_(std::move(irq_info)),
irq_status_(0) {}
// Note: The out_pin_index returned by this API is not the index of the pin
// in the particular GPIO block. eg. if its 7, its not GPIOH7
// It is the index of the bit corresponding to the GPIO in consideration in a
// particular INPUT/OUTPUT/PULL-UP/PULL-DOWN/PULL-ENABLE/ENABLE register
// out_block and out_lock are owned by the AmlGxlGpio instance and should not be deleted by
// callers.
zx_status_t AmlPinToBlock(const uint32_t pin, const AmlGpioBlock** out_block,
uint32_t* out_pin_index) const;
void Bind(const pbus_protocol_t& pbus);
uint32_t Read32GpioReg(int index, uint32_t offset) TA_REQ(mmio_lock_) {
return mmios_[index].Read32(offset << 2);
}
void Write32GpioReg(int index, uint32_t offset, uint32_t value) TA_REQ(mmio_lock_) {
mmios_[index].Write32(value, offset << 2);
}
uint32_t Read32GpioInterruptReg(uint32_t offset) TA_REQ(mmio_lock_) {
return mmio_interrupt_.Read32(offset << 2);
}
void Write32GpioInterruptReg(uint32_t offset, uint32_t value) TA_REQ(mmio_lock_) {
mmio_interrupt_.Write32(value, offset << 2);
}
pdev_protocol_t pdev_;
fbl::Mutex mmio_lock_;
ddk::MmioBuffer mmios_[2] TA_GUARDED(mmio_lock_); // separate MMIO for AO domain
ddk::MmioBuffer mmio_interrupt_ TA_GUARDED(mmio_lock_);
const AmlGpioBlock* gpio_blocks_;
const AmlGpioInterrupt* gpio_interrupt_;
const AmlPinMuxBlock* pinmux_blocks_;
size_t block_count_;
fbl::Mutex irq_lock_ TA_ACQ_BEFORE(mmio_lock_);
fbl::Array<uint16_t> irq_info_ TA_GUARDED(irq_lock_);
uint8_t irq_status_ TA_GUARDED(irq_lock_);
};
} // namespace gpio
#endif // SRC_DEVICES_GPIO_DRIVERS_AML_GXL_GPIO_AML_GXL_GPIO_H_