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