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 "aml-gxl-gpio.h"
6 
7 #include <ddk/debug.h>
8 #include <ddk/platform-defs.h>
9 #include <ddk/protocol/platform/bus.h>
10 #include <ddk/protocol/platform/device.h>
11 #include <ddk/protocol/platform-device-lib.h>
12 #include <fbl/alloc_checker.h>
13 #include <fbl/array.h>
14 #include <fbl/auto_lock.h>
15 #include <fbl/mutex.h>
16 #include <fbl/unique_ptr.h>
17 
18 #include <utility>
19 
20 #include "s905-blocks.h"
21 #include "s905x-blocks.h"
22 #include "s912-blocks.h"
23 
24 namespace {
25 
26 constexpr int kGpioInterruptPolarityShift = 16;
27 constexpr int kMaxGpioIndex = 255;
28 constexpr int kBitsPerGpioInterrupt = 8;
29 constexpr int kBitsPerFilterSelect = 4;
30 
GetUnusedIrqIndex(uint8_t status)31 uint32_t GetUnusedIrqIndex(uint8_t status) {
32     // First isolate the rightmost 0-bit
33     uint8_t zero_bit_set = static_cast<uint8_t>(~status & (status + 1));
34     // Count no. of leading zeros
35     return __builtin_ctz(zero_bit_set);
36 }
37 
38 }  // namespace
39 
40 namespace gpio {
41 
42 // MMIO indices (based on vim-gpio.c gpio_mmios)
43 enum {
44     MMIO_GPIO            = 0,
45     MMIO_GPIO_A0         = 1,
46     MMIO_GPIO_INTERRUPTS = 2,
47 };
48 
Create(zx_device_t * parent)49 zx_status_t AmlGxlGpio::Create(zx_device_t* parent) {
50     zx_status_t status;
51 
52     pdev_protocol_t pdev;
53     if ((status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev)) != ZX_OK) {
54         zxlogf(ERROR, "AmlGxlGpio::Create: ZX_PROTOCOL_PDEV not available\n");
55         return status;
56     }
57 
58     pbus_protocol_t pbus;
59     if ((status = device_get_protocol(parent, ZX_PROTOCOL_PBUS, &pbus)) != ZX_OK) {
60         zxlogf(ERROR, "AmlGxlGpio::Create: ZX_PROTOCOL_PBUS not available\n");
61         return status;
62     }
63 
64     mmio_buffer_t mmio;
65     status = pdev_map_mmio_buffer2(&pdev, MMIO_GPIO, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
66     if (status != ZX_OK) {
67         zxlogf(ERROR, "AmlGxlGpio::Create: pdev_map_mmio_buffer failed\n");
68         return status;
69     }
70 
71     ddk::MmioBuffer mmio_gpio(mmio);
72 
73     status = pdev_map_mmio_buffer2(&pdev, MMIO_GPIO_A0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
74     if (status != ZX_OK) {
75         zxlogf(ERROR, "AmlGxlGpio::Create: pdev_map_mmio_buffer failed\n");
76         return status;
77     }
78 
79     ddk::MmioBuffer mmio_gpio_a0(mmio);
80 
81     status = pdev_map_mmio_buffer2(&pdev, MMIO_GPIO_INTERRUPTS, ZX_CACHE_POLICY_UNCACHED_DEVICE,
82                                    &mmio);
83     if (status != ZX_OK) {
84         zxlogf(ERROR, "AmlGxlGpio::Create: pdev_map_mmio_buffer failed\n");
85         return status;
86     }
87 
88     ddk::MmioBuffer mmio_interrupt(mmio);
89 
90     pdev_device_info_t info;
91     if ((status = pdev_get_device_info(&pdev, &info)) != ZX_OK) {
92         zxlogf(ERROR, "AmlGxlGpio::Create: pdev_get_device_info failed\n");
93         return status;
94     }
95 
96     const AmlGpioBlock* gpio_blocks;
97     const AmlPinMuxBlock* pinmux_blocks;
98     const AmlGpioInterrupt* gpio_interrupt;
99     size_t block_count;
100 
101     switch (info.pid) {
102     case PDEV_PID_AMLOGIC_S912:
103         gpio_blocks = s912_gpio_blocks;
104         pinmux_blocks = s912_pinmux_blocks;
105         gpio_interrupt = &s912_interrupt_block;
106         block_count = countof(s912_gpio_blocks);
107         break;
108     case PDEV_PID_AMLOGIC_S905X:
109         gpio_blocks = s905x_gpio_blocks;
110         pinmux_blocks = s905x_pinmux_blocks;
111         gpio_interrupt = &s905x_interrupt_block;
112         block_count = countof(s905x_gpio_blocks);
113         break;
114     case PDEV_PID_AMLOGIC_S905:
115         gpio_blocks = s905_gpio_blocks;
116         pinmux_blocks = s905_pinmux_blocks;
117         gpio_interrupt = &s905_interrupt_block;
118         block_count = countof(s905_gpio_blocks);
119         break;
120     default:
121         zxlogf(ERROR, "AmlGxlGpio::Create: unsupported SOC PID %u\n", info.pid);
122         return ZX_ERR_INVALID_ARGS;
123     }
124 
125     fbl::AllocChecker ac;
126 
127     fbl::Array<uint16_t> irq_info(new (&ac) uint16_t[info.irq_count], info.irq_count);
128     if (!ac.check()) {
129         zxlogf(ERROR, "AmlGxlGpio::Create: irq_info alloc failed\n");
130         return ZX_ERR_NO_MEMORY;
131     }
132 
133     fbl::Array<fbl::Mutex> block_locks(new (&ac) fbl::Mutex[block_count], block_count);
134     if (!ac.check()) {
135         zxlogf(ERROR, "AmlGxlGpio::Create: block locks alloc failed\n");
136         return ZX_ERR_NO_MEMORY;
137     }
138 
139     fbl::unique_ptr<AmlGxlGpio> device(new (&ac) AmlGxlGpio(parent,
140                                                             pdev,
141                                                             std::move(mmio_gpio),
142                                                             std::move(mmio_gpio_a0),
143                                                             std::move(mmio_interrupt),
144                                                             gpio_blocks,
145                                                             gpio_interrupt,
146                                                             pinmux_blocks,
147                                                             block_count,
148                                                             std::move(block_locks),
149                                                             std::move(irq_info)));
150     if (!ac.check()) {
151         zxlogf(ERROR, "AmlGxlGpio::Create: device object alloc failed\n");
152         return ZX_ERR_NO_MEMORY;
153     }
154 
155     device->Bind(pbus);
156 
157     if ((status = device->DdkAdd("aml-gxl-gpio")) != ZX_OK) {
158         zxlogf(ERROR, "AmlGxlGpio::Create: DdkAdd failed\n");
159         return status;
160     }
161 
162     __UNUSED auto* unused = device.release();
163 
164     return ZX_OK;
165 }
166 
Bind(const pbus_protocol_t & pbus)167 void AmlGxlGpio::Bind(const pbus_protocol_t& pbus) {
168     gpio_impl_protocol_t gpio_proto = {
169         .ops = &gpio_impl_protocol_ops_,
170         .ctx = this
171     };
172 
173     const platform_proxy_cb_t kCallback = {NULL, NULL};
174     pbus_register_protocol(&pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio_proto, sizeof(gpio_proto),
175                            &kCallback);
176 }
177 
AmlPinToBlock(const uint32_t pin,const AmlGpioBlock ** out_block,uint32_t * out_pin_index,fbl::Mutex ** out_lock) const178 zx_status_t AmlGxlGpio::AmlPinToBlock(const uint32_t pin, const AmlGpioBlock** out_block,
179                                       uint32_t* out_pin_index, fbl::Mutex** out_lock) const {
180     ZX_DEBUG_ASSERT(out_block && out_pin_index);
181 
182     uint32_t block_index = pin / kPinsPerBlock;
183     if (block_index >= block_count_) {
184         return ZX_ERR_NOT_FOUND;
185     }
186     const AmlGpioBlock* block = &gpio_blocks_[block_index];
187     uint32_t pin_index = pin % kPinsPerBlock;
188     if (pin_index >= block->pin_count) {
189         return ZX_ERR_NOT_FOUND;
190     }
191     pin_index += block->output_shift;
192     *out_block = block;
193     *out_pin_index = pin_index;
194     *out_lock = &block_locks_[block_index];
195     return ZX_OK;
196 }
197 
GpioImplConfigIn(uint32_t index,uint32_t flags)198 zx_status_t AmlGxlGpio::GpioImplConfigIn(uint32_t index, uint32_t flags) {
199     zx_status_t status;
200 
201     const AmlGpioBlock* block;
202     uint32_t pin_index;
203     fbl::Mutex* block_lock;
204     if ((status = AmlPinToBlock(index, &block, &pin_index, &block_lock)) != ZX_OK) {
205         zxlogf(ERROR, "AmGxlGpio::GpioImplConfigIn: pin not found %u\n", index);
206         return status;
207     }
208 
209     // Set the GPIO as IN or OUT
210     fbl::AutoLock al(block_lock);
211     uint32_t regval = Read32GpioReg(block->mmio_index, block->oen_offset);
212     // Set the GPIO as pull-up or pull-down
213     uint32_t pull_reg_val = Read32GpioReg(block->mmio_index, block->pull_offset);
214     uint32_t pull_en_reg_val = Read32GpioReg(block->mmio_index, block->pull_en_offset);
215     uint32_t pull_pin_index = pin_index;
216     if (block->output_write_shift) {
217         // Handling special case where output_offset is
218         // different for OEN/OUT/PU-PD for GPIOA0 block
219         pull_pin_index += block->output_write_shift;
220     }
221     if (flags & GPIO_NO_PULL) {
222         pull_en_reg_val &= ~(1 << pin_index);
223     } else {
224         if (flags & GPIO_PULL_UP) {
225             pull_reg_val |= (1 << pull_pin_index);
226         } else {
227             pull_reg_val &= ~(1 << pull_pin_index);
228         }
229         pull_en_reg_val |= (1 << pin_index);
230     }
231     Write32GpioReg(block->mmio_index, block->pull_offset, pull_reg_val);
232     Write32GpioReg(block->mmio_index, block->pull_en_offset, pull_en_reg_val);
233     regval |= (1 << pin_index);
234     Write32GpioReg(block->mmio_index, block->oen_offset, regval);
235 
236     return ZX_OK;
237 }
238 
GpioImplConfigOut(uint32_t index,uint8_t initial_value)239 zx_status_t AmlGxlGpio::GpioImplConfigOut(uint32_t index, uint8_t initial_value) {
240     zx_status_t status;
241 
242     const AmlGpioBlock* block;
243     uint32_t pin_index;
244     fbl::Mutex* block_lock;
245     if ((status = AmlPinToBlock(index, &block, &pin_index, &block_lock)) != ZX_OK) {
246         zxlogf(ERROR, "AmlGxlGpio::GpioImplConfigOut: pin not found %u\n", index);
247         return status;
248     }
249 
250     fbl::AutoLock al(block_lock);
251 
252     // Set value before configuring for output
253     uint32_t regval = Read32GpioReg(block->mmio_index, block->output_offset);
254     // output_write_shift is handling special case where output_offset is
255     // different for OEN/OUT for GPIOA0 block
256     if (initial_value) {
257         regval |= (1 << (pin_index + block->output_write_shift));
258     } else {
259         regval &= ~(1 << (pin_index + block->output_write_shift));
260     }
261     Write32GpioReg(block->mmio_index, block->output_offset, regval);
262 
263     regval = Read32GpioReg(block->mmio_index, block->oen_offset);
264     regval &= ~(1 << pin_index);
265     Write32GpioReg(block->mmio_index, block->oen_offset, regval);
266 
267     return ZX_OK;
268 }
269 
GpioImplSetAltFunction(uint32_t pin,uint64_t function)270 zx_status_t AmlGxlGpio::GpioImplSetAltFunction(uint32_t pin, uint64_t function) {
271     if (function > kAltFunctionMax) {
272         return ZX_ERR_OUT_OF_RANGE;
273     }
274 
275     uint32_t block_index = pin / kPinsPerBlock;
276     if (block_index >= block_count_) {
277         return ZX_ERR_NOT_FOUND;
278     }
279     const AmlPinMuxBlock* block = &pinmux_blocks_[block_index];
280     uint32_t pin_index = pin % kPinsPerBlock;
281     const AmlPinMux* mux = &block->mux[pin_index];
282 
283     const AmlGpioBlock* gpio_block = &gpio_blocks_[block_index];
284     fbl::AutoLock al(&pinmux_lock_);
285 
286     for (uint64_t i = 0; i < kAltFunctionMax; i++) {
287         uint32_t reg_index = mux->regs[i];
288 
289         if (reg_index) {
290             uint32_t mask = (1 << mux->bits[i]);
291             uint32_t regval = Read32GpioReg(gpio_block->mmio_index, reg_index);
292 
293             if (i == function - 1) {
294                 regval |= mask;
295             } else {
296                 regval &= ~mask;
297             }
298             Write32GpioReg(gpio_block->mmio_index, reg_index, regval);
299         }
300     }
301 
302     return ZX_OK;
303 }
304 
GpioImplRead(uint32_t pin,uint8_t * out_value)305 zx_status_t AmlGxlGpio::GpioImplRead(uint32_t pin, uint8_t* out_value) {
306     zx_status_t status;
307 
308     const AmlGpioBlock* block;
309     uint32_t pin_index;
310     fbl::Mutex* block_lock;
311     if ((status = AmlPinToBlock(pin, &block, &pin_index, &block_lock)) != ZX_OK) {
312         zxlogf(ERROR, "AmGxlGpio::GpioImplRead: pin not found %u\n", pin);
313         return status;
314     }
315 
316     const uint32_t readmask = 1 << pin_index;
317     block_lock->Acquire();
318 
319     const uint32_t regval = Read32GpioReg(block->mmio_index, block->input_offset);
320 
321     block_lock->Release();
322 
323     if (regval & readmask) {
324         *out_value = 1;
325     } else {
326         *out_value = 0;
327     }
328 
329     return ZX_OK;
330 }
331 
GpioImplWrite(uint32_t pin,uint8_t value)332 zx_status_t AmlGxlGpio::GpioImplWrite(uint32_t pin, uint8_t value) {
333     zx_status_t status;
334 
335     const AmlGpioBlock* block;
336     uint32_t pin_index;
337     fbl::Mutex* block_lock;
338     if ((status = AmlPinToBlock(pin, &block, &pin_index, &block_lock)) != ZX_OK) {
339         zxlogf(ERROR, "AmlGxlGpio::GpioImplWrite: pin not found %u\n", pin);
340         return status;
341     }
342 
343     if (block->output_write_shift) {
344         // Handling special case where output_offset is
345         // different for OEN/OUT for GPIOA0 block
346         pin_index += block->output_write_shift;
347     }
348 
349     fbl::AutoLock al(block_lock);
350 
351     uint32_t regval = Read32GpioReg(block->mmio_index, block->output_offset);
352     if (value) {
353         regval |= (1 << pin_index);
354     } else {
355         regval &= ~(1 << pin_index);
356     }
357     Write32GpioReg(block->mmio_index, block->output_offset, regval);
358 
359     return ZX_OK;
360 }
361 
GpioImplGetInterrupt(uint32_t pin,uint32_t flags,zx::interrupt * out_irq)362 zx_status_t AmlGxlGpio::GpioImplGetInterrupt(uint32_t pin, uint32_t flags,
363                                              zx::interrupt* out_irq) {
364     if (pin > kMaxGpioIndex) {
365         return ZX_ERR_INVALID_ARGS;
366     }
367 
368     fbl::AutoLock al(&interrupt_lock_);
369 
370     uint32_t index = GetUnusedIrqIndex(irq_status_);
371     if (index > irq_info_.size()) {
372         return ZX_ERR_NO_RESOURCES;
373     }
374 
375     for (uint32_t i = 0; i < irq_info_.size(); i++) {
376         if (irq_info_[i] == pin) {
377             zxlogf(ERROR, "GPIO Interrupt already configured for this pin %u\n", (int)index);
378             return ZX_ERR_ALREADY_EXISTS;
379         }
380     }
381 
382     zxlogf(INFO, "GPIO Interrupt index %d allocated\n", (int)index);
383 
384     zx_status_t status;
385     const AmlGpioBlock* block;
386     uint32_t pin_index;
387     fbl::Mutex* block_lock;
388     if ((status = AmlPinToBlock(pin, &block, &pin_index, &block_lock)) != ZX_OK) {
389         zxlogf(ERROR, "AmlGxlGpio::GpioImplGetInterrupt: pin not found %u\n", pin);
390         return status;
391     }
392 
393     uint32_t flags_ = flags;
394     if (flags == ZX_INTERRUPT_MODE_EDGE_LOW) {
395         // GPIO controller sets the polarity
396         flags_ = ZX_INTERRUPT_MODE_EDGE_HIGH;
397     } else if (flags == ZX_INTERRUPT_MODE_LEVEL_LOW) {
398         flags_ = ZX_INTERRUPT_MODE_LEVEL_HIGH;
399     }
400 
401     // Create Interrupt Object
402     if ((status = pdev_get_interrupt(&pdev_, index, flags_,
403                                      out_irq->reset_and_get_address())) != ZX_OK) {
404         zxlogf(ERROR, "AmlGxlGpio::GpioImplGetInterrupt: pdev_map_interrupt failed %d\n", status);
405         return status;
406     }
407 
408     // Configure GPIO interrupt
409     uint32_t pin_select_offset = (index > 3) ? gpio_interrupt_->pin_4_7_select_offset
410                                              : gpio_interrupt_->pin_0_3_select_offset;
411 
412     // Select GPIO IRQ(index) and program it to
413     // the requested GPIO PIN
414     mmio_interrupt_.ModifyBits((pin % kPinsPerBlock) + block->pin_start,
415                                index * kBitsPerGpioInterrupt, kBitsPerGpioInterrupt,
416                                pin_select_offset << 2);
417     // Configure GPIO Interrupt EDGE and Polarity
418     uint32_t mode_reg_val = Read32GpioInterruptReg(gpio_interrupt_->edge_polarity_offset);
419 
420     switch (flags & ZX_INTERRUPT_MODE_MASK) {
421     case ZX_INTERRUPT_MODE_EDGE_LOW:
422         mode_reg_val = mode_reg_val | (1 << index);
423         mode_reg_val = mode_reg_val | ((1 << index) << kGpioInterruptPolarityShift);
424         break;
425     case ZX_INTERRUPT_MODE_EDGE_HIGH:
426         mode_reg_val = mode_reg_val | (1 << index);
427         mode_reg_val = mode_reg_val & ~((1 << index) << kGpioInterruptPolarityShift);
428         break;
429     case ZX_INTERRUPT_MODE_LEVEL_LOW:
430         mode_reg_val = mode_reg_val & ~(1 << index);
431         mode_reg_val = mode_reg_val | ((1 << index) << kGpioInterruptPolarityShift);
432         break;
433     case ZX_INTERRUPT_MODE_LEVEL_HIGH:
434         mode_reg_val = mode_reg_val & ~(1 << index);
435         mode_reg_val = mode_reg_val & ~((1 << index) << kGpioInterruptPolarityShift);
436         break;
437     default:
438         return ZX_ERR_INVALID_ARGS;
439     }
440     Write32GpioInterruptReg(gpio_interrupt_->edge_polarity_offset, mode_reg_val);
441 
442     // Configure Interrupt Select Filter
443     uint32_t regval = Read32GpioInterruptReg(gpio_interrupt_->filter_select_offset);
444     Write32GpioInterruptReg(gpio_interrupt_->filter_select_offset,
445                             regval | (0x7 << (index * kBitsPerFilterSelect)));
446     irq_status_ |= static_cast<uint8_t>(1 << index);
447     irq_info_[index] = static_cast<uint16_t>(pin);
448 
449     return ZX_OK;
450 }
451 
GpioImplReleaseInterrupt(uint32_t pin)452 zx_status_t AmlGxlGpio::GpioImplReleaseInterrupt(uint32_t pin) {
453     fbl::AutoLock al(&interrupt_lock_);
454 
455     for (uint32_t i = 0; i < irq_info_.size(); i++) {
456         if (irq_info_[i] == pin) {
457             irq_status_ &= static_cast<uint8_t>(~(1 << i));
458             irq_info_[i] = kMaxGpioIndex + 1;
459             return ZX_OK;
460         }
461     }
462 
463     return ZX_ERR_NOT_FOUND;
464 }
465 
GpioImplSetPolarity(uint32_t pin,uint32_t polarity)466 zx_status_t AmlGxlGpio::GpioImplSetPolarity(uint32_t pin, uint32_t polarity) {
467     int irq_index = -1;
468     if (pin > kMaxGpioIndex) {
469         return ZX_ERR_INVALID_ARGS;
470     }
471 
472     for (uint32_t i = 0; i < irq_info_.size(); i++) {
473         if (irq_info_[i] == pin) {
474             irq_index = i;
475             break;
476         }
477     }
478 
479     if (irq_index == -1) {
480         return ZX_ERR_NOT_FOUND;
481     }
482 
483     fbl::AutoLock al(&interrupt_lock_);
484 
485     // Configure GPIO Interrupt EDGE and Polarity
486     uint32_t mode_reg_val = Read32GpioInterruptReg(gpio_interrupt_->edge_polarity_offset);
487     if (polarity == GPIO_POLARITY_HIGH) {
488         mode_reg_val &= ~((1 << irq_index) << kGpioInterruptPolarityShift);
489     } else {
490         mode_reg_val |= ((1 << irq_index) << kGpioInterruptPolarityShift);
491     }
492 
493     Write32GpioInterruptReg(gpio_interrupt_->edge_polarity_offset, mode_reg_val);
494 
495     return ZX_OK;
496 }
497 
498 }  // namespace gpio
499 
aml_gpio_bind(void * ctx,zx_device_t * parent)500 extern "C" zx_status_t aml_gpio_bind(void* ctx, zx_device_t* parent) {
501     return gpio::AmlGxlGpio::Create(parent);
502 }
503