| // 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 |