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