blob: 09f130e168e67838e6ac72d8202d1469af8cb9ae [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 <lib/mmio/mmio.h>
#include <zircon/types.h>
#include <hwreg/bitfields.h>
#include <soc/mt8167/mt8167-hw.h>
namespace {
// There are 2 sets of GPIO Pull settings register banks, those under GPIO and those under IOCFG.
// Those under the GPIO have a consistent numbering mapping such that the register offsets
// can be calculated based on the GPIO number. The GPIOs that fall into IOCFG are marked as '0'
// in kGpioPullInGpioRegs and return false in GpioPullEnReg/GpioPullSelReg methods to indicate that
// they are not supported in the GPIO registers so we then try IoConfigReg methods. Note that the
// last 3 GPIO numbers in the array don't fall under GPIO or IOCFG (as any other number past 127).
static constexpr bool kGpioPullInGpioRegs[][16] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, // 0 (first GPIO in this line).
{0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, // 16.
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}, // 32.
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 48.
{1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, // 64.
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 80.
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 96.
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0}, // 112 (first is 112, last is 127).
};
} // namespace
namespace gpio {
enum PullAmount {
kNoPull,
kPull10K,
kPull50K,
kPull10K50K,
kPull75K,
kPull2K,
kPull75K2K,
kPull200K,
kPull75K200K,
};
// GPIO MODE defines PINMUX for this device
class GpioModeReg : public hwreg::RegisterBase<GpioModeReg, uint16_t> {
public:
static constexpr uint16_t kModeGpio = 0; // GPIO mode is always 0
static constexpr uint64_t kModeMax = 8; // 3 bits per mode
static uint16_t GetMode(ddk::MmioBuffer* mmio, size_t idx) {
return Read(mmio, idx).GetMode(idx % kItemsPerReg);
}
static void SetMode(ddk::MmioBuffer* mmio, size_t idx, uint16_t value) {
Read(mmio, idx).SetMode(idx % kItemsPerReg, value).WriteTo(mmio);
}
private:
static constexpr size_t kItemsPerReg = 5;
// Registers are 16 bits, separated by 0x10 bytes, with kItemsPerReg values per register.
static uint32_t Idx2Offset(size_t idx) {
return static_cast<uint32_t>(0x300 + (idx / kItemsPerReg) * 0x10);
}
uint16_t GetMode(size_t offset) const {
switch (offset) {
case 0:
return mode0();
case 1:
return mode1();
case 2:
return mode2();
case 3:
return mode3();
}
return mode4();
}
GpioModeReg& SetMode(size_t offset, uint16_t value) {
switch (offset) {
case 0:
return set_mode0(value);
case 1:
return set_mode1(value);
case 2:
return set_mode2(value);
case 3:
return set_mode3(value);
}
return set_mode4(value);
}
static GpioModeReg Read(ddk::MmioBuffer* mmio, size_t idx) {
return hwreg::RegisterAddr<GpioModeReg>(Idx2Offset(idx)).ReadFrom(mmio);
}
DEF_FIELD(14, 12, mode4);
DEF_FIELD(11, 9, mode3);
DEF_FIELD(8, 6, mode2);
DEF_FIELD(5, 3, mode1);
DEF_FIELD(2, 0, mode0);
};
class GpioBitFieldView : public ddk::MmioView {
// Registers are 16 bits and separated by 0x10 bytes.
uint32_t Idx2Offset(size_t idx) const { return static_cast<uint32_t>((idx / 16) * 0x10); }
public:
explicit GpioBitFieldView(ddk::MmioBuffer& mmio, zx_off_t offset, size_t size)
: ddk::MmioView(mmio.View(offset, size)) {}
void ModifyBit(size_t idx, bool val) const {
ddk::MmioView::ModifyBit<uint16_t>(val, idx % 16, Idx2Offset(idx));
}
uint16_t GetBit(size_t idx) const {
return ddk::MmioView::GetBit<uint16_t>(idx % 16, Idx2Offset(idx));
}
};
class GpioDirReg : public GpioBitFieldView {
public:
explicit GpioDirReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 0, 0x100) {}
void SetDir(size_t idx, bool is_out) const { ModifyBit(idx, is_out); }
};
class GpioOutReg : public GpioBitFieldView {
public:
explicit GpioOutReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 0x100, 0x100) {}
void SetVal(size_t idx, bool val) const { ModifyBit(idx, val); }
};
class GpioInReg : public GpioBitFieldView {
public:
explicit GpioInReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 0x200, 0x100) {}
uint16_t GetVal(size_t idx) const { return GetBit(idx); }
};
class GpioPullEnReg : public GpioBitFieldView {
public:
explicit GpioPullEnReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 0x500, 0x100) {}
bool PullEnable(size_t idx) const { return PullEnableInternal(idx, true); }
bool PullDisable(size_t idx) const { return PullEnableInternal(idx, false); }
private:
bool PullEnableInternal(size_t idx, bool val) const {
if (idx >= sizeof(kGpioPullInGpioRegs) / (sizeof(**kGpioPullInGpioRegs)) ||
!kGpioPullInGpioRegs[idx / 16][idx % 16]) {
return false;
}
ModifyBit(idx, val);
return true;
}
};
class GpioPullSelReg : public GpioBitFieldView {
public:
explicit GpioPullSelReg(ddk::MmioBuffer& mmio) : GpioBitFieldView(mmio, 0x600, 0x100) {}
bool SetPullUp(size_t idx) const { return SetPullInternal(idx, true); }
bool SetPullDown(size_t idx) const { return SetPullInternal(idx, false); }
private:
bool SetPullInternal(size_t idx, bool up) const {
if (idx >= sizeof(kGpioPullInGpioRegs) / (sizeof(**kGpioPullInGpioRegs)) ||
!kGpioPullInGpioRegs[idx / 16][idx % 16]) {
return false;
}
ModifyBit(idx, up);
return true;
}
};
class IoConfigReg : public ddk::MmioBuffer {
public:
explicit IoConfigReg(ddk::MmioBuffer mmio) : ddk::MmioBuffer(std::move(mmio)) {}
bool SetPullUp(size_t idx) const { return SetPullInternal(idx, true); }
bool SetPullDown(size_t idx) const { return SetPullInternal(idx, false); }
bool PullEnable(size_t idx, PullAmount amount) const { return PullEnableInternal(idx, amount); }
bool PullDisable(size_t idx) const { return PullEnableInternal(idx, kNoPull); }
private:
// This list pull settings not in the GPIO register set, but only here in IOCFG.
static constexpr struct {
size_t idx;
uint32_t reg_offset;
uint32_t up_down_bit;
uint32_t pull_bit_start;
PullAmount pull_amount_sets_reg_to_1;
PullAmount pull_amount_sets_reg_to_2;
PullAmount pull_amount_sets_reg_to_3;
} pull_regs[] = {
// clang-format off
{ 14, 0x550, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 15, 0x560, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 16, 0x560, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 17, 0x560, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 21, 0x560, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 22, 0x570, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 23, 0x570, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 40, 0x580, 2, 0, kPull75K, kPull2K, kPull75K2K},
{ 41, 0x580, 6, 4, kPull75K, kPull2K, kPull75K2K},
{ 42, 0x590, 2, 0, kPull75K, kPull200K, kPull75K200K},
{ 43, 0x590, 6, 4, kPull75K, kPull200K, kPull75K200K},
{ 68, 0x550, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 69, 0x550, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 70, 0x540, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 71, 0x540, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 72, 0x540, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 73, 0x550, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 104, 0x540, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 105, 0x530, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 106, 0x520, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 107, 0x530, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 108, 0x530, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 109, 0x530, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 110, 0x510, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 111, 0x510, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 112, 0x510, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 113, 0x510, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 114, 0x520, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 115, 0x520, 2, 0, kPull10K, kPull50K, kPull10K50K},
{ 116, 0x520, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 117, 0x500, 14, 12, kPull10K, kPull50K, kPull10K50K},
{ 118, 0x500, 10, 8, kPull10K, kPull50K, kPull10K50K},
{ 119, 0x500, 6, 4, kPull10K, kPull50K, kPull10K50K},
{ 120, 0x500, 2, 0, kPull10K, kPull50K, kPull10K50K},
// clang-format on
};
bool SetPullInternal(size_t idx, bool up) const {
for (auto& i : pull_regs) {
if (i.idx == idx) {
ModifyBit<uint16_t>(!up, i.up_down_bit, i.reg_offset);
return true;
}
}
return false;
}
bool PullEnableInternal(size_t idx, PullAmount pull) const {
for (auto& i : pull_regs) {
if (i.idx == idx) {
uint16_t val;
if (pull == kNoPull) {
val = 0;
} else if (pull == i.pull_amount_sets_reg_to_1) {
val = 1;
} else if (pull == i.pull_amount_sets_reg_to_2) {
val = 2;
} else if (pull == i.pull_amount_sets_reg_to_3) {
val = 3;
} else {
return false; // Not supported pull amount for this GPIO.
}
ModifyBits<uint16_t>(val, i.pull_bit_start, 2, i.reg_offset);
return true;
}
}
return false;
}
};
class ExtendedInterruptReg : public ddk::MmioBuffer {
public:
explicit ExtendedInterruptReg(ddk::MmioBuffer mmio) : ddk::MmioBuffer(std::move(mmio)) {}
void Enable(size_t idx) const { EnableInternal(idx, true); }
void Disable(size_t idx) const { EnableInternal(idx, false); }
bool IsEnabled(size_t idx) const {
return !GetBit<uint32_t>(idx % kBitsPerReg, 0x80 + (idx / kBitsPerReg) * kBytesRegSeparation);
}
void SetPolarity(size_t idx, bool high) const {
SetBit<uint32_t>(idx % kBitsPerReg,
(high ? 0x340 : 0x380) + (idx / kBitsPerReg) * kBytesRegSeparation);
}
void SetEdge(size_t idx, bool edge) const {
SetBit<uint32_t>(idx % kBitsPerReg,
(edge ? 0x1C0 : 0x180) + (idx / kBitsPerReg) * kBytesRegSeparation);
}
void SetDomain0(size_t idx) const {
// These registers are not described on the reference manual.
SetBit<uint32_t>(idx % kBitsPerReg, 0x400 + (idx / kBitsPerReg) * kBytesRegSeparation);
}
void AckInterrupt(size_t idx) const {
// These registers are not described on the reference manual.
SetBit<uint32_t>(idx % kBitsPerReg, 0x040 + (idx / kBitsPerReg) * kBytesRegSeparation);
}
uint32_t GetNextInterrupt(uint32_t start) const {
for (uint32_t i = start; i < MT8167_GPIO_EINT_MAX; i += kBitsPerReg) {
// These registers are not described on the reference manual.
auto reg = Read<uint32_t>(0x000 + (i / kBitsPerReg) * kBytesRegSeparation);
if (reg) {
return i + 31 - __builtin_clz(reg);
}
}
return kInvalidInterruptIdx;
}
static constexpr uint32_t kInvalidInterruptIdx = static_cast<uint32_t>(-1);
private:
void EnableInternal(size_t idx, bool enable) const {
SetBit<uint32_t>(idx % kBitsPerReg,
(enable ? 0x100 : 0xC0) + (idx / kBitsPerReg) * kBytesRegSeparation);
}
static constexpr uint32_t kBitsPerReg = 32;
static constexpr uint32_t kBytesRegSeparation = 4;
};
} // namespace gpio