1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <ddktl/mmio.h> 6 7 #include <hwreg/bitfields.h> 8 #include <hwreg/mmio.h> 9 10 #include <zircon/types.h> 11 12 namespace gpio { 13 14 constexpr bool kGpioPullValid[][16] = { 15 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 16 {0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, 17 {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}, 18 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 19 {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, 20 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 21 {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 22 {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0}, 23 }; 24 25 // GPIO MODE defines PINMUX for this device 26 class GpioModeReg : public hwreg::RegisterBase<GpioModeReg, uint16_t> { 27 public: 28 static constexpr uint16_t kModeGpio = 0; // GPIO mode is always 0 29 static constexpr uint64_t kModeMax = 8; // 3 bits per mode 30 GetMode(ddk::MmioBuffer * mmio,size_t idx)31 static uint16_t GetMode(ddk::MmioBuffer* mmio, size_t idx) { 32 return Read(mmio, idx).GetMode(idx % kItemsPerReg); 33 } 34 SetMode(ddk::MmioBuffer * mmio,size_t idx,uint16_t value)35 static void SetMode(ddk::MmioBuffer* mmio, size_t idx, uint16_t value) { 36 Read(mmio, idx).SetMode(idx % kItemsPerReg, value).WriteTo(mmio); 37 } 38 39 private: 40 static constexpr size_t kItemsPerReg = 5; 41 42 // Registers are 16 bits, separated by 0x10 bytes, with kItemsPerReg values per register. Idx2Offset(size_t idx)43 static uint32_t Idx2Offset(size_t idx) { 44 return static_cast<uint32_t>(0x300 + (idx / kItemsPerReg) * 0x10); 45 } 46 GetMode(size_t offset)47 uint16_t GetMode(size_t offset) const { 48 switch (offset) { 49 case 0: 50 return mode0(); 51 case 1: 52 return mode1(); 53 case 2: 54 return mode2(); 55 case 3: 56 return mode3(); 57 } 58 return mode4(); 59 } 60 SetMode(size_t offset,uint16_t value)61 GpioModeReg& SetMode(size_t offset, uint16_t value) { 62 switch (offset) { 63 case 0: 64 return set_mode0(value); 65 case 1: 66 return set_mode1(value); 67 case 2: 68 return set_mode2(value); 69 case 3: 70 return set_mode3(value); 71 } 72 return set_mode4(value); 73 } 74 Read(ddk::MmioBuffer * mmio,size_t idx)75 static GpioModeReg Read(ddk::MmioBuffer* mmio, size_t idx) { 76 return hwreg::RegisterAddr<GpioModeReg>(Idx2Offset(idx)).ReadFrom(mmio); 77 } 78 79 DEF_FIELD(14, 12, mode4); 80 DEF_FIELD(11, 9, mode3); 81 DEF_FIELD(8, 6, mode2); 82 DEF_FIELD(5, 3, mode1); 83 DEF_FIELD(2, 0, mode0); 84 }; 85 86 class GpioBitFieldView : public ddk::MmioView { 87 // Registers are 16 bits and separated by 0x10 bytes. Idx2Offset(size_t idx)88 uint32_t Idx2Offset(size_t idx) const { return static_cast<uint32_t>((idx / 16) * 0x10); } 89 90 public: GpioBitFieldView(mmio_buffer_t & mmio,zx_off_t offset,size_t size)91 explicit GpioBitFieldView(mmio_buffer_t& mmio, zx_off_t offset, size_t size) 92 : ddk::MmioView(mmio, offset, size) {} 93 ModifyBit(size_t idx,bool val)94 void ModifyBit(size_t idx, bool val) const { 95 ddk::MmioView::ModifyBit<uint16_t>(val, idx % 16, Idx2Offset(idx)); 96 } 97 GetBit(size_t idx)98 uint16_t GetBit(size_t idx) const { 99 return ddk::MmioView::GetBit<uint16_t>(idx % 16, Idx2Offset(idx)); 100 } 101 }; 102 103 class GpioDirReg : public GpioBitFieldView { 104 public: GpioDirReg(mmio_buffer_t & mmio)105 explicit GpioDirReg(mmio_buffer_t& mmio) 106 : GpioBitFieldView(mmio, 0, 0x100) {} SetDir(size_t idx,bool is_out)107 void SetDir(size_t idx, bool is_out) const { ModifyBit(idx, is_out); } 108 }; 109 110 class GpioOutReg : public GpioBitFieldView { 111 public: GpioOutReg(mmio_buffer_t & mmio)112 explicit GpioOutReg(mmio_buffer_t& mmio) 113 : GpioBitFieldView(mmio, 0x100, 0x100) {} SetVal(size_t idx,bool val)114 void SetVal(size_t idx, bool val) const { ModifyBit(idx, val); } 115 }; 116 117 class GpioInReg : public GpioBitFieldView { 118 public: GpioInReg(mmio_buffer_t & mmio)119 explicit GpioInReg(mmio_buffer_t& mmio) 120 : GpioBitFieldView(mmio, 0x200, 0x100) {} GetVal(size_t idx)121 uint16_t GetVal(size_t idx) const { return GetBit(idx); } 122 }; 123 124 class GpioPullEnReg : public GpioBitFieldView { 125 public: GpioPullEnReg(mmio_buffer_t & mmio)126 explicit GpioPullEnReg(mmio_buffer_t& mmio) 127 : GpioBitFieldView(mmio, 0x500, 0x100) {} 128 PullEnable(size_t idx)129 bool PullEnable(size_t idx) const { return PullEnableInternal(idx, true); } PullDisable(size_t idx)130 bool PullDisable(size_t idx) const { return PullEnableInternal(idx, false); } 131 132 private: PullEnableInternal(size_t idx,bool val)133 bool PullEnableInternal(size_t idx, bool val) const { 134 if (idx >= countof(kGpioPullValid) || !kGpioPullValid[idx / 16][idx % 16]) { 135 return false; 136 } 137 ModifyBit(idx, val); 138 return true; 139 } 140 }; 141 142 class GpioPullSelReg : public GpioBitFieldView { 143 public: GpioPullSelReg(mmio_buffer_t & mmio)144 explicit GpioPullSelReg(mmio_buffer_t& mmio) 145 : GpioBitFieldView(mmio, 0x600, 0x100) {} SetPullUp(size_t idx)146 bool SetPullUp(size_t idx) const { return SetPullInternal(idx, true); } SetPullDown(size_t idx)147 bool SetPullDown(size_t idx) const { return SetPullInternal(idx, false); } 148 149 private: SetPullInternal(size_t idx,bool up)150 bool SetPullInternal(size_t idx, bool up) const { 151 if (idx >= countof(kGpioPullValid) || !kGpioPullValid[idx / 16][idx % 16]) { 152 return false; 153 } 154 ModifyBit(idx, up); 155 return true; 156 } 157 }; 158 159 class IoConfigReg : public ddk::MmioBuffer { 160 public: IoConfigReg(mmio_buffer_t & mmio)161 explicit IoConfigReg(mmio_buffer_t& mmio) 162 : ddk::MmioBuffer(mmio) {} SetPullUp(size_t idx)163 bool SetPullUp(size_t idx) const { return SetPullInternal(idx, true); } SetPullDown(size_t idx)164 bool SetPullDown(size_t idx) const { return SetPullInternal(idx, false); } PullEnable(size_t idx)165 bool PullEnable(size_t idx) const { return PullEnableInternal(idx, true); } PullDisable(size_t idx)166 bool PullDisable(size_t idx) const { return PullEnableInternal(idx, false); } 167 168 private: SetPullInternal(size_t idx,bool up)169 bool SetPullInternal(size_t idx, bool up) const { 170 switch (idx) { 171 case 40: 172 ModifyBit<uint32_t>(!up, 2, 0x580); 173 return true; 174 case 41: 175 ModifyBit<uint32_t>(!up, 6, 0x580); 176 return true; 177 case 42: 178 ModifyBit<uint32_t>(!up, 2, 0x590); 179 return true; 180 case 43: 181 ModifyBit<uint32_t>(!up, 6, 0x590); 182 return true; 183 } 184 return false; 185 } PullEnableInternal(size_t idx,bool enable)186 bool PullEnableInternal(size_t idx, bool enable) const { 187 constexpr uint32_t r75K = 1; 188 switch (idx) { 189 case 40: 190 ModifyBits(enable ? r75K : 0, 0, 2, 0x580); 191 return true; 192 case 41: 193 ModifyBits(enable ? r75K : 0, 4, 2, 0x580); 194 return true; 195 case 42: 196 ModifyBits(enable ? r75K : 0, 0, 2, 0x590); 197 return true; 198 case 43: 199 ModifyBits(enable ? r75K : 0, 4, 2, 0x590); 200 return true; 201 } 202 return false; 203 } 204 }; 205 206 class ExtendedInterruptReg : public ddk::MmioBuffer { 207 public: ExtendedInterruptReg(mmio_buffer_t & mmio)208 explicit ExtendedInterruptReg(mmio_buffer_t& mmio) 209 : ddk::MmioBuffer(mmio) {} Enable(size_t idx)210 void Enable(size_t idx) const { EnableInternal(idx, true); } Disable(size_t idx)211 void Disable(size_t idx) const { EnableInternal(idx, false); } IsEnabled(size_t idx)212 bool IsEnabled(size_t idx) const { 213 return !GetBit<uint32_t>( 214 idx % kBitsPerReg, 0x80 + (idx / kBitsPerReg) * kBytesRegSeparation); 215 } SetPolarity(size_t idx,bool high)216 void SetPolarity(size_t idx, bool high) const { 217 SetBit<uint32_t>(idx % kBitsPerReg, 218 (high ? 0x340 : 0x380) + (idx / kBitsPerReg) * kBytesRegSeparation); 219 } SetEdge(size_t idx,bool edge)220 void SetEdge(size_t idx, bool edge) const { 221 SetBit<uint32_t>(idx % kBitsPerReg, 222 (edge ? 0x1C0 : 0x180) + (idx / kBitsPerReg) * kBytesRegSeparation); 223 } 224 SetDomain0(size_t idx)225 void SetDomain0(size_t idx) const { 226 // These registers are not described on the reference manual. 227 SetBit<uint32_t>(idx % kBitsPerReg, 0x400 + (idx / kBitsPerReg) * kBytesRegSeparation); 228 } AckInterrupt(size_t idx)229 void AckInterrupt(size_t idx) const { 230 // These registers are not described on the reference manual. 231 SetBit<uint32_t>(idx % kBitsPerReg, 0x040 + (idx / kBitsPerReg) * kBytesRegSeparation); 232 } GetNextInterrupt(uint32_t start)233 uint32_t GetNextInterrupt(uint32_t start) const { 234 for (uint32_t i = start; i < MT8167_GPIO_EINT_MAX; i += kBitsPerReg) { 235 // These registers are not described on the reference manual. 236 auto reg = Read<uint32_t>( 237 0x000 + (i / kBitsPerReg) * kBytesRegSeparation); 238 if (reg) { 239 return i + 31 - __builtin_clz(reg); 240 } 241 } 242 return kInvalidInterruptIdx; 243 } 244 245 static constexpr uint32_t kInvalidInterruptIdx = static_cast<uint32_t>(-1); 246 247 private: EnableInternal(size_t idx,bool enable)248 void EnableInternal(size_t idx, bool enable) const { 249 SetBit<uint32_t>(idx % kBitsPerReg, 250 (enable ? 0x100 : 0xC0) + (idx / kBitsPerReg) * kBytesRegSeparation); 251 } 252 static constexpr uint32_t kBitsPerReg = 32; 253 static constexpr uint32_t kBytesRegSeparation = 4; 254 }; 255 } // namespace gpio 256