blob: 302255410032dab9ad36661c8e0d9c998fca381f [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 <lib/mmio/mmio.h>
#include <zircon/types.h>
#include <hwreg/bitfields.h>
namespace gpio {
constexpr uint32_t kGpioMax = 142;
// GPIO Cfg defines PINMUX for this device
class GpioCfgReg : public hwreg::RegisterBase<GpioCfgReg, uint32_t> {
public:
static constexpr uint32_t kModeGpio = 0; // GPIO mode is always 0
static constexpr uint64_t kModeMax = 16; // 4 bits per mode
static uint32_t GetMode(ddk::MmioBuffer* mmio, size_t idx) { return Read(mmio, idx).FUNC_SEL(); }
static void SetMode(ddk::MmioBuffer* mmio, size_t idx, uint32_t value) {
Read(mmio, idx).set_FUNC_SEL(value).WriteTo(mmio);
}
static void SetOut(ddk::MmioBuffer* mmio, size_t idx, bool output) {
Read(mmio, idx).set_GPIO_OE(static_cast<uint32_t>(output)).WriteTo(mmio);
}
static void SetPullNone(ddk::MmioBuffer* mmio, size_t idx) { SetPull(mmio, idx, 0); }
static void SetPullDown(ddk::MmioBuffer* mmio, size_t idx) { SetPull(mmio, idx, 1); }
static void SetPullUp(ddk::MmioBuffer* mmio, size_t idx) { SetPull(mmio, idx, 3); }
static void SetStrength(ddk::MmioBuffer* mmio, size_t idx, uint8_t mA) {
Read(mmio, idx).set_DRV_STRENGTH(mA / 2 - 1).WriteTo(mmio);
}
protected:
// Registers are separated by 0x1000 bytes.
static uint32_t Idx2Offset(size_t idx) { return static_cast<uint32_t>(idx * 0x1000); }
private:
static GpioCfgReg Read(ddk::MmioBuffer* mmio, size_t idx) {
return hwreg::RegisterAddr<GpioCfgReg>(Idx2Offset(idx)).ReadFrom(mmio);
}
static void SetPull(ddk::MmioBuffer* mmio, size_t idx, uint32_t reg_value) {
Read(mmio, idx).set_GPIO_PULL(reg_value).WriteTo(mmio);
}
DEF_BIT(10, GPIO_HIHYS_EN);
DEF_BIT(9, GPIO_OE);
DEF_FIELD(8, 6, DRV_STRENGTH);
DEF_FIELD(5, 2, FUNC_SEL);
DEF_FIELD(1, 0, GPIO_PULL);
};
class GpioBitFieldView : public ddk::MmioView {
public:
explicit GpioBitFieldView(ddk::MmioBuffer& mmio, zx_off_t offset, size_t size)
: ddk::MmioView(mmio.View(offset, size)) {}
protected:
// Registers are separated by 0x1000 bytes.
uint32_t Idx2Offset(size_t idx) const { return static_cast<uint32_t>(idx * 0x1000); }
};
class GpioInOutReg : public GpioBitFieldView {
public:
explicit GpioInOutReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 4, 0x1000 * kGpioMax) {}
bool GetVal(size_t idx) const {
return static_cast<bool>(ddk::MmioView::GetBit<uint32_t>(0, Idx2Offset(idx)));
}
void SetVal(size_t idx, bool val) const {
ModifyBit<uint32_t>(static_cast<uint32_t>(val), 1, Idx2Offset(idx));
}
};
enum class Mode { EdgeLow, EdgeHigh, EdgeDual, LevelLow, LevelHigh };
class GpioIntCfgReg : public GpioBitFieldView {
public:
explicit GpioIntCfgReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 8, 0x1000 * kGpioMax) {}
void EnableCombined(size_t idx, bool val) const {
uint32_t target_proc = 0x7; // NONE. Don't route to any processor subsystem.
if (val) {
target_proc = 0x4; // APPS. Route the GPIO[n] signal to APSS summary interrupt.
}
ModifyBit<uint32_t>(static_cast<uint32_t>(val), 4, Idx2Offset(idx)); // Raw status.
ModifyBits<uint32_t>(target_proc, 5, 3, Idx2Offset(idx));
ModifyBit<uint32_t>(static_cast<uint32_t>(val), 0, Idx2Offset(idx)); // Enable.
}
void EnableDirect(size_t idx, bool val) const {
ModifyBit<uint32_t>(static_cast<uint32_t>(val), 8, Idx2Offset(idx));
}
void SetMode(size_t idx, Mode mode) const {
uint32_t detect_reg = 0;
uint32_t polarity_reg = 0;
// clang-format off
switch (mode) {
case Mode::EdgeHigh: detect_reg = 0x1; polarity_reg = 1; break;
case Mode::EdgeLow: detect_reg = 0x2; polarity_reg = 1; break;
case Mode::EdgeDual: detect_reg = 0x3; break; // polarity_reg is don't care.
case Mode::LevelHigh: detect_reg = 0x0; polarity_reg = 1; break;
case Mode::LevelLow: detect_reg = 0x0; polarity_reg = 0; break;
}
// clang-format on
ModifyBits<uint32_t>(detect_reg, 2, 2, Idx2Offset(idx));
ModifyBit<uint32_t>(polarity_reg, 1, Idx2Offset(idx));
}
void SetPolarity(size_t idx, bool high) const {
uint32_t detect_reg = GetBits<uint32_t>(2, 2, Idx2Offset(idx));
uint32_t polarity_reg = GetBit<uint32_t>(1, Idx2Offset(idx));
Mode old_mode;
// clang-format off
switch (detect_reg) {
case 0x1: old_mode = Mode::EdgeHigh; break;
case 0x2: old_mode = Mode::EdgeLow; break;
case 0x3: old_mode = Mode::EdgeDual; break;
case 0x0: old_mode = polarity_reg ? Mode::LevelHigh : Mode::LevelLow; break;
}
switch (old_mode) {
case Mode::EdgeHigh: SetMode(idx, high ? Mode::EdgeHigh : Mode::EdgeLow); break;
case Mode::EdgeLow: SetMode(idx, high ? Mode::EdgeHigh : Mode::EdgeLow); break;
case Mode::EdgeDual: break; // noop.
case Mode::LevelHigh: SetMode(idx, high ? Mode::LevelHigh : Mode::LevelLow); break;
case Mode::LevelLow: SetMode(idx, high ? Mode::LevelHigh : Mode::LevelLow); break;
}
// clang-format on
}
};
class TlmmDirConnIntReg : public GpioBitFieldView {
protected:
// Registers are separated by 4 bytes.
uint32_t Idx2Offset(size_t idx) const { return static_cast<uint32_t>(idx * 4); }
public:
explicit TlmmDirConnIntReg(ddk::MmioBuffer& mmio)
: GpioBitFieldView(mmio, 0x102000, 4 * kGpioMax) {}
void Enable(size_t gpio_n, size_t dir_int_n) const {
ModifyBits<uint32_t>(static_cast<uint32_t>(gpio_n), 0, 8, Idx2Offset(dir_int_n));
}
};
class TlmmGpioIntrStatusReg : public GpioBitFieldView {
public:
explicit TlmmGpioIntrStatusReg(ddk::MmioBuffer& mmio)
: GpioBitFieldView(mmio, 0xC, 0x1000 * kGpioMax) {}
bool Status(size_t gpio_n) const {
return static_cast<bool>(GetBit<uint32_t>(0, Idx2Offset(gpio_n)));
}
void Clear(size_t gpio_n) const { ClearBit<uint32_t>(0, Idx2Offset(gpio_n)); }
};
} // namespace gpio