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 #pragma once
6 
7 #include <ddk/protocol/platform/bus.h>
8 #include <ddktl/device.h>
9 #include <ddktl/mmio.h>
10 #include <ddktl/protocol/gpioimpl.h>
11 #include <ddktl/protocol/platform/device.h>
12 #include <fbl/array.h>
13 #include <fbl/mutex.h>
14 #include <fbl/vector.h>
15 #include <hw/reg.h>
16 #include <inttypes.h>
17 
18 #include <utility>
19 
20 namespace gpio {
21 
22 constexpr int kPinsPerBlock = 32;
23 constexpr uint64_t kAltFunctionMax = 6;
24 
25 struct AmlGpioBlock {
26     uint32_t pin_count;
27     uint32_t oen_offset;
28     uint32_t input_offset;
29     uint32_t output_offset;
30     uint32_t output_shift;  // Used for GPIOAO block
31     uint32_t output_write_shift = 0;
32     uint32_t mmio_index;
33     uint32_t pull_offset;
34     uint32_t pull_en_offset;
35     uint32_t pin_start;
36 };
37 
38 struct AmlPinMux {
39     // pinmux register offsets for the alternate functions.
40     // zero means alternate function not supported.
41     uint8_t regs[kAltFunctionMax];
42     // bit number to set/clear to enable/disable alternate function
43     uint8_t bits[kAltFunctionMax];
44 };
45 
46 struct AmlPinMuxBlock {
47     AmlPinMux mux[kPinsPerBlock];
48 };
49 
50 struct AmlGpioInterrupt {
51     uint32_t pin_0_3_select_offset;
52     uint32_t pin_4_7_select_offset;
53     uint32_t edge_polarity_offset;
54     uint32_t filter_select_offset;
55     uint32_t status_offset;
56     uint32_t mask_offset;
57 };
58 
59 class AmlGxlGpio;
60 using DeviceType = ddk::Device<AmlGxlGpio, ddk::Unbindable>;
61 
62 class AmlGxlGpio : public DeviceType,
63                    public ddk::GpioImplProtocol<AmlGxlGpio, ddk::base_protocol> {
64 
65 public:
66     static zx_status_t Create(zx_device_t* parent);
67 
68     zx_status_t GpioImplConfigIn(uint32_t index, uint32_t flags);
69     zx_status_t GpioImplConfigOut(uint32_t index, uint8_t initial_value);
70     zx_status_t GpioImplSetAltFunction(uint32_t pin, uint64_t function);
71     zx_status_t GpioImplRead(uint32_t pin, uint8_t* out_value);
72     zx_status_t GpioImplWrite(uint32_t pin, uint8_t value);
73     zx_status_t GpioImplGetInterrupt(uint32_t pin, uint32_t flags, zx::interrupt* out_irq);
74     zx_status_t GpioImplReleaseInterrupt(uint32_t pin);
75     zx_status_t GpioImplSetPolarity(uint32_t pin, uint32_t polarity);
76 
DdkUnbind()77     void DdkUnbind() { DdkRemove(); }
DdkRelease()78     void DdkRelease() { delete this; }
79 
80 private:
AmlGxlGpio(zx_device_t * parent,const pdev_protocol_t & pdev,ddk::MmioBuffer mmio_gpio,ddk::MmioBuffer mmio_gpio_a0,ddk::MmioBuffer mmio_interrupt,const AmlGpioBlock * gpio_blocks,const AmlGpioInterrupt * gpio_interrupt,const AmlPinMuxBlock * pinmux_blocks,size_t block_count,fbl::Array<fbl::Mutex> block_locks,fbl::Array<uint16_t> irq_info)81     AmlGxlGpio(zx_device_t* parent, const pdev_protocol_t& pdev,
82                ddk::MmioBuffer mmio_gpio, ddk::MmioBuffer mmio_gpio_a0,
83                ddk::MmioBuffer mmio_interrupt, const AmlGpioBlock* gpio_blocks,
84                const AmlGpioInterrupt* gpio_interrupt, const AmlPinMuxBlock* pinmux_blocks,
85                size_t block_count, fbl::Array<fbl::Mutex> block_locks,
86                fbl::Array<uint16_t> irq_info)
87         : DeviceType(parent), pdev_(pdev), mmios_{std::move(mmio_gpio), std::move(mmio_gpio_a0)},
88           mmio_interrupt_(std::move(mmio_interrupt)), gpio_blocks_(gpio_blocks),
89           gpio_interrupt_(gpio_interrupt), pinmux_blocks_(pinmux_blocks), block_count_(block_count),
90           block_locks_(std::move(block_locks)), irq_info_(std::move(irq_info)), irq_status_(0) {}
91 
92     // Note: The out_pin_index returned by this API is not the index of the pin
93     // in the particular GPIO block. eg. if its 7, its not GPIOH7
94     // It is the index of the bit corresponding to the GPIO in consideration in a
95     // particular INPUT/OUTPUT/PULL-UP/PULL-DOWN/PULL-ENABLE/ENABLE register
96     // out_block and out_lock are owned by the AmlGxlGpio instance and should not be deleted by
97     // callers.
98     zx_status_t AmlPinToBlock(const uint32_t pin, const AmlGpioBlock** out_block,
99                               uint32_t* out_pin_index, fbl::Mutex** out_lock) const;
100 
101     void Bind(const pbus_protocol_t& pbus);
102 
Read32GpioReg(int index,uint32_t offset)103     inline uint32_t Read32GpioReg(int index, uint32_t offset) {
104         return mmios_[index].Read32(offset << 2);
105     }
106 
Write32GpioReg(int index,uint32_t offset,uint32_t value)107     inline void Write32GpioReg(int index, uint32_t offset, uint32_t value) {
108         mmios_[index].Write32(value, offset << 2);
109     }
110 
Read32GpioInterruptReg(uint32_t offset)111     inline uint32_t Read32GpioInterruptReg(uint32_t offset) {
112         return mmio_interrupt_.Read32(offset << 2);
113     }
114 
Write32GpioInterruptReg(uint32_t offset,uint32_t value)115     inline void Write32GpioInterruptReg(uint32_t offset, uint32_t value) {
116         mmio_interrupt_.Write32(value, offset << 2);
117     }
118 
119     pdev_protocol_t pdev_;
120     ddk::MmioBuffer mmios_[2];  // separate MMIO for AO domain
121     ddk::MmioBuffer mmio_interrupt_;
122     const AmlGpioBlock* gpio_blocks_;
123     const AmlGpioInterrupt* gpio_interrupt_;
124     const AmlPinMuxBlock* pinmux_blocks_;
125     size_t block_count_;
126     const fbl::Array<fbl::Mutex> block_locks_;
127     fbl::Mutex interrupt_lock_;
128     fbl::Mutex pinmux_lock_;
129     fbl::Array<uint16_t> irq_info_;
130     uint8_t irq_status_;
131 };
132 
133 }  // namespace gpio
134