[gpio][interrupts] Add Interrupt support for GPIOs Change-Id: Ib2657d24247614641f3f1d809f0371fd653d9417
diff --git a/system/dev/board/aml-s905d2/aml-gpio.c b/system/dev/board/aml-s905d2/aml-gpio.c index 7883764..bb29c61 100644 --- a/system/dev/board/aml-s905d2/aml-gpio.c +++ b/system/dev/board/aml-s905d2/aml-gpio.c
@@ -61,6 +61,7 @@ .irq = S905D2_GPIO_IRQ_7, .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, + /* { .irq = S905D2_A0_GPIO_IRQ_0, .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, @@ -69,6 +70,7 @@ .irq = S905D2_A0_GPIO_IRQ_1, .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, + */ }; static pbus_dev_t gpio_dev = {
diff --git a/system/dev/board/vim/vim-gpio.c b/system/dev/board/vim/vim-gpio.c index f8385c1..b0764be 100644 --- a/system/dev/board/vim/vim-gpio.c +++ b/system/dev/board/vim/vim-gpio.c
@@ -28,49 +28,43 @@ .base = S912_GPIO_A0_BASE, .length = S912_GPIO_AO_LENGTH, }, + { + .base = S912_GPIO_INTERRUPT_BASE, + .length = S912_GPIO_INTERRUPT_LENGTH, + }, }; // S905X and S912 have same GPIO IRQ numbers static const pbus_irq_t gpio_irqs[] = { { .irq = S912_GPIO_IRQ_0, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_1, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_2, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_3, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_4, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_5, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_6, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_GPIO_IRQ_7, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_A0_GPIO_IRQ_0, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, { .irq = S912_A0_GPIO_IRQ_1, - .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, }, }; @@ -113,6 +107,12 @@ // SYS_LED .gpio = (bus->soc_pid == PDEV_PID_AMLOGIC_S912 ? S912_GPIOAO(9) : S905X_GPIOAO(9)), }, + { + .gpio = S912_GPIOH(7), + }, + { + .gpio = S912_GPIOH(5), + } }; const pbus_dev_t gpio_test_dev = {
diff --git a/system/dev/board/vim/vim.c b/system/dev/board/vim/vim.c index 6708b9c..fb69015 100644 --- a/system/dev/board/vim/vim.c +++ b/system/dev/board/vim/vim.c
@@ -63,7 +63,7 @@ const pbus_gpio_t vim_display_gpios[] = { { // HPD - .gpio = S912_GPIOH(20), + .gpio = S912_GPIOH(0), }, };
diff --git a/system/dev/bus/platform/platform-device.c b/system/dev/bus/platform/platform-device.c index 0d110e1..6794265 100644 --- a/system/dev/bus/platform/platform-device.c +++ b/system/dev/bus/platform/platform-device.c
@@ -58,15 +58,21 @@ return status; } -static zx_status_t platform_dev_map_interrupt(void* ctx, uint32_t index, zx_handle_t* out_handle) { +static zx_status_t platform_dev_map_interrupt(void* ctx, uint32_t index, + uint32_t flags, zx_handle_t* out_handle) { platform_dev_t* dev = ctx; - + uint32_t flags_; if (index >= dev->irq_count || !out_handle) { return ZX_ERR_INVALID_ARGS; } pbus_irq_t* irq = &dev->irqs[index]; + if (flags) { + flags_ = flags; + } else { + flags_ = irq->mode; + } #if ENABLE_NEW_IRQ_API - zx_status_t status = zx_irq_create(dev->bus->resource, irq->irq, irq->mode, out_handle); + zx_status_t status = zx_irq_create(dev->bus->resource, irq->irq, flags_, out_handle); if (status != ZX_OK) { zxlogf(ERROR, "platform_dev_map_interrupt: zx_irq_create failed %d\n", status); return status; @@ -77,7 +83,7 @@ zxlogf(ERROR, "platform_dev_map_interrupt: zx_interrupt_create failed %d\n", status); return status; } - status = zx_interrupt_bind(*out_handle, 0, dev->bus->resource, irq->irq, irq->mode); + status = zx_interrupt_bind(*out_handle, 0, dev->bus->resource, irq->irq, flags_); if (status != ZX_OK) { zxlogf(ERROR, "platform_dev_map_interrupt: zx_interrupt_bind failed %d\n", status); zx_handle_close(*out_handle); @@ -149,9 +155,10 @@ } static zx_status_t pdev_rpc_get_interrupt(platform_dev_t* dev, uint32_t index, + uint32_t flags, zx_handle_t* out_handle, uint32_t* out_handle_count) { - zx_status_t status = platform_dev_map_interrupt(dev, index, out_handle); + zx_status_t status = platform_dev_map_interrupt(dev, index, flags, out_handle); if (status == ZX_OK) { *out_handle_count = 1; } @@ -238,6 +245,26 @@ return gpio_write(&bus->gpio, index, value); } +static zx_status_t pdev_rpc_get_gpio_interrupt(platform_dev_t* dev, uint32_t index, + uint32_t flags, + zx_handle_t* out_handle, + uint32_t* out_handle_count) { + platform_bus_t* bus = dev->bus; + if (!bus->gpio.ops) { + return ZX_ERR_NOT_SUPPORTED; + } + if (index >= dev->gpio_count) { + return ZX_ERR_INVALID_ARGS; + } + + index = dev->gpios[index].gpio; + zx_status_t status = gpio_get_interrupt(&bus->gpio, index, flags, out_handle); + if (status == ZX_OK) { + *out_handle_count = 1; + } + return status; +} + static zx_status_t pdev_rpc_i2c_transact(platform_dev_t* dev, pdev_req_t* req, uint8_t* data, zx_handle_t channel) { platform_bus_t* bus = dev->bus; @@ -312,7 +339,7 @@ &handle, &handle_count); break; case PDEV_GET_INTERRUPT: - resp.status = pdev_rpc_get_interrupt(dev, req->index, &handle, &handle_count); + resp.status = pdev_rpc_get_interrupt(dev, req->index, req->flags, &handle, &handle_count); break; case PDEV_GET_BTI: resp.status = pdev_rpc_get_bti(dev, req->index, &handle, &handle_count); @@ -338,6 +365,9 @@ case PDEV_GPIO_WRITE: resp.status = pdev_rpc_gpio_write(dev, req->index, req->gpio_value); break; + case PDEV_GPIO_GET_INTERRUPT: + resp.status = pdev_rpc_get_gpio_interrupt(dev, req->index, req->flags, &handle, &handle_count); + break; case PDEV_I2C_GET_MAX_TRANSFER: resp.status = i2c_impl_get_max_transfer_size(&dev->bus->i2c, req->index, &resp.i2c_max_transfer);
diff --git a/system/dev/bus/platform/platform-proxy.c b/system/dev/bus/platform/platform-proxy.c index 1a5503f..54f18cd 100644 --- a/system/dev/bus/platform/platform-proxy.c +++ b/system/dev/bus/platform/platform-proxy.c
@@ -126,6 +126,20 @@ return platform_dev_rpc(proxy, &req, sizeof(req), &resp, sizeof(resp), NULL, 0, NULL); } +static zx_status_t pdev_gpio_get_interrupt(void* ctx, uint32_t index, + uint32_t flags, + zx_handle_t *out_handle) { + platform_proxy_t* proxy = ctx; + pdev_req_t req = { + .op = PDEV_GPIO_GET_INTERRUPT, + .index = index, + .flags = flags, + }; + pdev_resp_t resp; + + return platform_dev_rpc(proxy, &req, sizeof(req), &resp, sizeof(resp), + out_handle, 1, NULL); +} static zx_status_t pdev_gpio_read(void* ctx, uint32_t index, uint8_t* out_value) { platform_proxy_t* proxy = ctx; pdev_req_t req = { @@ -160,6 +174,7 @@ .set_alt_function = pdev_gpio_set_alt_function, .read = pdev_gpio_read, .write = pdev_gpio_write, + .get_interrupt = pdev_gpio_get_interrupt, }; static zx_status_t pdev_i2c_get_max_transfer_size(void* ctx, uint32_t index, size_t* out_size) { @@ -323,11 +338,13 @@ return status; } -static zx_status_t platform_dev_map_interrupt(void* ctx, uint32_t index, zx_handle_t* out_handle) { +static zx_status_t platform_dev_map_interrupt(void* ctx, uint32_t index, + uint32_t flags, zx_handle_t* out_handle) { platform_proxy_t* proxy = ctx; pdev_req_t req = { .op = PDEV_GET_INTERRUPT, .index = index, + .flags = flags, }; pdev_resp_t resp;
diff --git a/system/dev/bus/platform/platform-proxy.h b/system/dev/bus/platform/platform-proxy.h index f40018d..6061a61 100644 --- a/system/dev/bus/platform/platform-proxy.h +++ b/system/dev/bus/platform/platform-proxy.h
@@ -30,6 +30,7 @@ PDEV_GPIO_SET_ALT_FUNCTION, PDEV_GPIO_READ, PDEV_GPIO_WRITE, + PDEV_GPIO_GET_INTERRUPT, // ZX_PROTOCOL_I2C PDEV_I2C_GET_MAX_TRANSFER, @@ -65,6 +66,7 @@ uint8_t gpio_value; pdev_i2c_txn_ctx_t i2c_txn; uint32_t i2c_bitrate; + uint32_t flags; }; } pdev_req_t;
diff --git a/system/dev/display/vim-display/vim-display.c b/system/dev/display/vim-display/vim-display.c index f8e8133..c6eb070 100644 --- a/system/dev/display/vim-display/vim-display.c +++ b/system/dev/display/vim-display/vim-display.c
@@ -265,48 +265,52 @@ return ZX_OK; } +static int hdmi_irq_handler(void *arg) { + vim2_display_t* display = arg; + while(1) { +#if ENABLE_NEW_IRQ_API + zx_status_t status = zx_irq_wait(display->inth, NULL); +#else + uint64_t slots; + zx_status_t status = zx_interrupt_wait(display->inth, &slots); +#endif + if (status != ZX_OK) { + printf("hdmi_irq_handler: Waiting failed %d\n", status); + return -1; + } + if (display->hdmi_inited) { + DISP_ERROR("Display Disconnected!\n"); + hdmi_shutdown(display); + io_buffer_release(&display->fbuffer); + device_remove(display->fbdevice); + } + if (ZX_OK == setup_hdmi(display)) { + display->hdmi_inited = true; + DISP_ERROR("Display is connected\n"); + } + } + return 0; +} + static int main_hdmi_thread(void *arg) { vim2_display_t* display = arg; - static bool hdmi_inited = false; - static bool print_once = true; - uint8_t hpd_val; + zx_status_t status; - if (gpio_config(&display->gpio, 0, GPIO_DIR_IN) != ZX_OK) { - DISP_ERROR("Invalid HPD Pin!! Will try and connect to display anyways\n"); - // try once - setup_hdmi(display); - return ZX_OK; + if (ZX_OK != (status = gpio_config(&display->gpio, 0, GPIO_DIR_IN))) { + DISP_ERROR("DISPLAY: gpio_config failed for gpio\n"); + return status; } - while (1) { - // check HPD GPIO Pins - gpio_read(&display->gpio, 0, &hpd_val); - - if (hpd_val == 0) { - if (print_once) { - DISP_ERROR("No Display Connected. Will try again later\n"); - print_once = false; - } - if (hdmi_inited) { - // let's shutdown hdmi - DISP_ERROR("Display Disconnected!\n"); - hdmi_shutdown(display); - io_buffer_release(&display->fbuffer); - device_remove(display->fbdevice); - hdmi_inited = false; - } - } else { - if (!hdmi_inited) { - DISP_ERROR("Display is connected\n"); - if (setup_hdmi(display) != ZX_OK) { - return ZX_ERR_UNAVAILABLE; - } - hdmi_inited = true; - } - } - usleep(500000); // sleep with 500ms + if (ZX_OK != (status = gpio_get_interrupt(&display->gpio, 0, + ZX_INTERRUPT_MODE_EDGE_HIGH, + &display->inth) != ZX_OK)) { + DISP_ERROR("DISPLAY: gpio_config failed for gpio\n"); + return status; } + + thrd_create_with_name(&display->main_thread, hdmi_irq_handler, display, "hdmi_irq_handler thread"); + return ZX_OK; } zx_status_t vim2_display_bind(void* ctx, zx_device_t* parent) {
diff --git a/system/dev/display/vim-display/vim-display.h b/system/dev/display/vim-display/vim-display.h index f2d1e90..575b2d2 100644 --- a/system/dev/display/vim-display/vim-display.h +++ b/system/dev/display/vim-display/vim-display.h
@@ -31,6 +31,7 @@ zx_device_t* mydevice; zx_device_t* fbdevice; zx_handle_t bti; + zx_handle_t inth; gpio_protocol_t gpio; @@ -57,6 +58,7 @@ disp_timing_t pref_disp_timing; bool console_visible; + bool hdmi_inited; zx_display_cb_t ownership_change_callback; void* ownership_change_cookie; } vim2_display_t;
diff --git a/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c b/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c index 1156fce..c42962a 100644 --- a/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c +++ b/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c
@@ -201,11 +201,19 @@ return ZX_OK; } +static zx_status_t aml_gpio_get_interrupt(void *ctx, uint32_t pin, + uint32_t flags, + zx_handle_t *out_handle) { + zxlogf(ERROR, "HELLO INTERRUPT AXG%u\n", pin); + return ZX_OK; +} + static gpio_protocol_ops_t gpio_ops = { .config = aml_gpio_config, .set_alt_function = aml_gpio_set_alt_function, .read = aml_gpio_read, .write = aml_gpio_write, + .get_interrupt = aml_gpio_get_interrupt, }; static void aml_gpio_release(void* ctx) {
diff --git a/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c b/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c index d4de42a..5915191 100644 --- a/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c +++ b/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c
@@ -14,13 +14,10 @@ #include <ddk/protocol/platform-defs.h> #include <ddk/protocol/platform-device.h> #include <hw/reg.h> - +#include "aml-gxl-gpio.h" #include <zircon/assert.h> #include <zircon/types.h> -#define PINS_PER_BLOCK 32 -#define ALT_FUNCTION_MAX 6 - typedef struct { uint32_t pin_count; uint32_t oen_offset; @@ -28,6 +25,9 @@ uint32_t output_offset; uint32_t output_shift; // Used for GPIOAO block uint32_t mmio_index; + uint32_t pull_offset; + uint32_t pull_en_offset; + uint32_t pin_start; mtx_t lock; } aml_gpio_block_t; @@ -48,12 +48,22 @@ gpio_protocol_t gpio; zx_device_t* zxdev; io_buffer_t mmios[2]; // separate MMIO for AO domain + io_buffer_t mmio_interrupt; aml_gpio_block_t* gpio_blocks; const aml_pinmux_block_t* pinmux_blocks; size_t block_count; mtx_t pinmux_lock; + uint32_t irq_count; + uint8_t irq_status; } aml_gpio_t; +// MMIO indices (based on vim2_display_mmios) +enum { + MMIO_GPIO, + MMIO_GPIO_A0, + MMIO_GPIO_INTERRUPTS, +}; + #include "s912-blocks.h" #include "s905x-blocks.h" #include "s905-blocks.h" @@ -71,7 +81,7 @@ if (pin_index >= block->pin_count) { return ZX_ERR_NOT_FOUND; } - + pin_index += block->output_shift; *out_block = block; *out_pin_index = pin_index; return ZX_OK; @@ -88,21 +98,36 @@ return status; } + // Set the GPIO as IN or OUT volatile uint32_t* reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmios[block->mmio_index]); reg += block->oen_offset; - mtx_lock(&block->lock); uint32_t regval = readl(reg); - - if (flags & GPIO_DIR_OUT) { + uint32_t direction = flags & GPIO_DIR_MASK; + if (direction & GPIO_DIR_OUT) { regval &= ~(1 << pin_index); } else { + // Set the GPIO as pull-up or pull-down + uint32_t pull = flags & GPIO_PULL_MASK; + volatile uint32_t* pull_reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmios[block->mmio_index]); + pull_reg += block->pull_offset; + volatile uint32_t* pull_en_reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmios[block->mmio_index]); + pull_en_reg += block->pull_en_offset; + + uint32_t pull_reg_val = readl(pull_reg); + uint32_t pull_en_reg_val = readl(pull_en_reg); + if (pull & GPIO_PULL_UP) { + pull_reg_val |= (1 << pin_index); + } else { + pull_reg_val &= ~(1 << pin_index); + } + pull_en_reg_val |= (1 << pin_index); + writel(pull_reg_val, pull_reg); + writel(pull_en_reg_val, pull_en_reg); regval |= (1 << pin_index); } - writel(regval, reg); - mtx_unlock(&block->lock); return ZX_OK; @@ -132,7 +157,7 @@ for (uint i = 0; i < ALT_FUNCTION_MAX; i++) { uint32_t reg_index = mux->regs[i]; - + //reg_index += block->output_shift; if (reg_index) { uint32_t mask = (1 << mux->bits[i]); uint32_t regval = readl(reg + reg_index); @@ -162,7 +187,6 @@ zxlogf(ERROR, "aml_gpio_read: pin not found %u\n", pin); return status; } - const uint32_t readmask = 1 << pin_index; volatile uint32_t* reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmios[block->mmio_index]); @@ -196,7 +220,6 @@ volatile uint32_t* reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmios[block->mmio_index]); reg += block->output_offset; - pin_index += block->output_shift; mtx_lock(&block->lock); @@ -215,18 +238,96 @@ return ZX_OK; } +static uint32_t aml_gpio_get_unsed_irq_index(uint8_t status) { + // First isolate the rightmost 0-bit + uint8_t zero_bit_set = ~status & (status+1); + // Count no. of leading zeros + return __builtin_ctz(zero_bit_set); +} + +static zx_status_t aml_gpio_get_interrupt(void *ctx, uint32_t pin, + uint32_t flags, + zx_handle_t *out_handle) { + aml_gpio_t* gpio = ctx; + zx_status_t status; + mtx_lock(&gpio->pinmux_lock); + if (pin > MAX_GPIO_INDEX) { + return ZX_ERR_INVALID_ARGS; + } + uint32_t index = aml_gpio_get_unsed_irq_index(gpio->irq_status); + if (index > gpio->irq_count) { + return ZX_ERR_NO_RESOURCES; + } + + aml_gpio_block_t* block; + uint32_t pin_index; + if ((status = aml_pin_to_block(gpio, pin, &block, &pin_index)) != ZX_OK) { + zxlogf(ERROR, "aml_gpio_read: pin not found %u\n", pin); + return status; + } + + // Create Interrupt Object + status = pdev_get_interrupt(&gpio->pdev, index, flags, + out_handle); + if (status != ZX_OK) { + zxlogf(ERROR, "aml_gpio_get_interrupt: pdev_map_interrupt failed %d\n", status); + return status; + } + + // Configure GPIO interrupt + volatile uint32_t* reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmio_interrupt); + reg += (index>3)? S912_GPIO_4_7_PIN_SELECT: S912_GPIO_0_3_PIN_SELECT; + + // Select GPIO IRQ(index) and program it to + // the requested GPIO PIN + uint32_t regval = readl(reg); + regval |= (((pin % PINS_PER_BLOCK) + block->pin_start) << (index * BITS_PER_GPIO_INTERRUPT)); + writel(regval, reg); + + // Configure GPIO Interrupt EDGE and Polarity + volatile uint32_t* mode_reg = (volatile uint32_t *)io_buffer_virt(&gpio->mmio_interrupt); + mode_reg += S912_GPIO_INT_EDGE_POLARITY; + uint32_t mode_reg_val = readl(mode_reg); + + switch (flags & ZX_INTERRUPT_MODE_MASK) { + case ZX_INTERRUPT_MODE_EDGE_LOW: + mode_reg_val = mode_reg_val | (1 << index); + mode_reg_val = mode_reg_val | ((1 << index) << GPIO_INTERRUPT_POLARITY_SHIFT); + break; + case ZX_INTERRUPT_MODE_EDGE_HIGH: + mode_reg_val = mode_reg_val | (1 << index); + mode_reg_val = mode_reg_val & ~((1 << index) << GPIO_INTERRUPT_POLARITY_SHIFT); + break; + case ZX_INTERRUPT_MODE_LEVEL_LOW: + mode_reg_val = mode_reg_val & ~(1 << index); + mode_reg_val = mode_reg_val | ((1 << index) << GPIO_INTERRUPT_POLARITY_SHIFT); + break; + case ZX_INTERRUPT_MODE_LEVEL_HIGH: + mode_reg_val = mode_reg_val & ~(1 << index); + mode_reg_val = mode_reg_val & ~((1 << index) << GPIO_INTERRUPT_POLARITY_SHIFT); + break; + default: + return ZX_ERR_INVALID_ARGS; + } + writel(mode_reg_val, mode_reg); + gpio->irq_status |= 1 << index; + mtx_unlock(&gpio->pinmux_lock); + return ZX_OK; +} + static gpio_protocol_ops_t gpio_ops = { .config = aml_gpio_config, .set_alt_function = aml_gpio_set_alt_function, .read = aml_gpio_read, .write = aml_gpio_write, + .get_interrupt = aml_gpio_get_interrupt, }; static void aml_gpio_release(void* ctx) { aml_gpio_t* gpio = ctx; - for (unsigned i = 0; i < countof(gpio->mmios); i++) { - io_buffer_release(&gpio->mmios[i]); - } + io_buffer_release(&gpio->mmios[0]); + io_buffer_release(&gpio->mmios[1]); + io_buffer_release(&gpio->mmio_interrupt); free(gpio); } @@ -256,13 +357,25 @@ goto fail; } - for (unsigned i = 0; i < countof(gpio->mmios); i++) { - status = pdev_map_mmio_buffer(&gpio->pdev, i, ZX_CACHE_POLICY_UNCACHED_DEVICE, - &gpio->mmios[i]); - if (status != ZX_OK) { - zxlogf(ERROR, "aml_gpio_bind: pdev_map_mmio_buffer failed\n"); - goto fail; - } + status = pdev_map_mmio_buffer(&gpio->pdev, MMIO_GPIO, ZX_CACHE_POLICY_UNCACHED_DEVICE, + &gpio->mmios[0]); + if (status != ZX_OK) { + zxlogf(ERROR, "aml_gpio_bind: pdev_map_mmio_buffer failed\n"); + goto fail; + } + + status = pdev_map_mmio_buffer(&gpio->pdev, MMIO_GPIO_A0, ZX_CACHE_POLICY_UNCACHED_DEVICE, + &gpio->mmios[1]); + if (status != ZX_OK) { + zxlogf(ERROR, "aml_gpio_bind: pdev_map_mmio_buffer failed\n"); + goto fail; + } + + status = pdev_map_mmio_buffer(&gpio->pdev, MMIO_GPIO_INTERRUPTS, ZX_CACHE_POLICY_UNCACHED_DEVICE, + &gpio->mmio_interrupt); + if (status != ZX_OK) { + zxlogf(ERROR, "aml_gpio_bind: pdev_map_mmio_buffer failed\n"); + goto fail; } pdev_device_info_t info; @@ -307,6 +420,8 @@ goto fail; } + gpio->irq_count = info.irq_count; + gpio->irq_status = 0; gpio->gpio.ops = &gpio_ops; gpio->gpio.ctx = gpio; pbus_set_protocol(&pbus, ZX_PROTOCOL_GPIO, &gpio->gpio);
diff --git a/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.h b/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.h new file mode 100644 index 0000000..9408f0b --- /dev/null +++ b/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.h
@@ -0,0 +1,17 @@ +// Copyright 2018 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +// These are relative to base address 0xc1100000 and in sizeof(uint32_t) +#define S912_GPIO_INT_EDGE_POLARITY 0x2620 +#define S912_GPIO_0_3_PIN_SELECT 0x2621 +#define S912_GPIO_4_7_PIN_SELECT 0x2622 +#define S912_GPIO_FILTER_SELECT 0x2623 + +#define GPIO_INTERRUPT_POLARITY_SHIFT 16 +#define PINS_PER_BLOCK 32 +#define ALT_FUNCTION_MAX 6 +#define MAX_GPIO_INDEX 255 +#define BITS_PER_GPIO_INTERRUPT 8
diff --git a/system/dev/gpio/aml-gxl-gpio/s912-blocks.h b/system/dev/gpio/aml-gxl-gpio/s912-blocks.h index e32166d..fb9d897 100644 --- a/system/dev/gpio/aml-gxl-gpio/s912-blocks.h +++ b/system/dev/gpio/aml-gxl-gpio/s912-blocks.h
@@ -13,6 +13,9 @@ .output_offset = S912_GPIOX_OUT, .output_shift = 0, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG4, + .pull_en_offset = S912_PULL_UP_EN_REG4, + .pin_start = S912_GPIOX_PIN_START, .lock = MTX_INIT, }, // GPIODV Block @@ -23,6 +26,9 @@ .output_offset = S912_GPIODV_OUT, .output_shift = 0, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG0, + .pull_en_offset = S912_PULL_UP_EN_REG0, + .pin_start = S912_GPIODV_PIN_START, .lock = MTX_INIT, }, // GPIOH Block @@ -31,8 +37,11 @@ .oen_offset = S912_GPIOH_0EN, .input_offset = S912_GPIOH_IN, .output_offset = S912_GPIOH_OUT, - .output_shift = 0, + .output_shift = 20, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG1, + .pull_en_offset = S912_PULL_UP_EN_REG1, + .pin_start = S912_GPIOH_PIN_START, .lock = MTX_INIT, }, // GPIOBOOT Block @@ -43,6 +52,9 @@ .output_offset = S912_GPIOBOOT_OUT, .output_shift = 0, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG2, + .pull_en_offset = S912_PULL_UP_EN_REG2, + .pin_start = S912_GPIOBOOT_PIN_START, .lock = MTX_INIT, }, // GPIOCARD Block @@ -51,8 +63,11 @@ .oen_offset = S912_GPIOCARD_0EN, .input_offset = S912_GPIOCARD_IN, .output_offset = S912_GPIOCARD_OUT, - .output_shift = 0, + .output_shift = 20, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG2, + .pull_en_offset = S912_PULL_UP_EN_REG2, + .pin_start = S912_GPIOCARD_PIN_START, .lock = MTX_INIT, }, // GPIOCLK Block @@ -61,8 +76,11 @@ .oen_offset = S912_GPIOCLK_0EN, .input_offset = S912_GPIOCLK_IN, .output_offset = S912_GPIOCLK_OUT, - .output_shift = 0, + .output_shift = 28, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG3, + .pull_en_offset = S912_PULL_UP_EN_REG3, + .pin_start = S912_GPIOCLK_PIN_START, .lock = MTX_INIT, }, // GPIOZ Block @@ -73,6 +91,9 @@ .output_offset = S912_GPIOZ_OUT, .output_shift = 0, .mmio_index = 0, + .pull_offset = S912_PULL_UP_REG3, + .pull_en_offset = S912_PULL_UP_EN_REG3, + .pin_start = S912_GPIOZ_PIN_START, .lock = MTX_INIT, }, // GPIOAO Block @@ -83,6 +104,9 @@ .output_offset = S912_AO_GPIO_OEN_OUT, .output_shift = 16, // output is shared with OEN .mmio_index = 1, + .pull_offset = 0, // not supported + .pull_en_offset = 0, // not supported + .pin_start = S912_GPIOA0_PIN_START, .lock = MTX_INIT, }, };
diff --git a/system/dev/gpio/gpio-test/gpio-test.c b/system/dev/gpio/gpio-test/gpio-test.c index e8d55ef..b4331a7 100644 --- a/system/dev/gpio/gpio-test/gpio-test.c +++ b/system/dev/gpio/gpio-test/gpio-test.c
@@ -27,7 +27,9 @@ gpio_protocol_t gpio; uint32_t gpio_count; thrd_t thread; + thrd_t wait; bool done; + zx_handle_t inth; } gpio_test_t; static void gpio_test_release(void* ctx) { @@ -68,6 +70,61 @@ return 0; } +// GPIO indices (based on gpio_test_gpios) +enum { + GPIO_LED, + GPIO_BUTTON, + GPIO_OUT_LED, +}; + +static int gpio_waiting_thread(void *arg) { + gpio_test_t* gpio_test = arg; + gpio_protocol_t* gpio = &gpio_test->gpio; + while(1) { + printf("gpio_waiting_thread Before WAITING %x\n", gpio_test->inth); +#if ENABLE_NEW_IRQ_API + zx_status_t status = zx_irq_wait(gpio_test->inth, NULL); +#else + uint64_t slots; + zx_status_t status = zx_interrupt_wait(gpio_test->inth, &slots); +#endif + if (status != ZX_OK) { + zxlogf(ERROR, "gpio_waiting_thread: Waiting failed %d\n", status); + return -1; + } + printf("gpio_waiting_thread After WAITING %x\n", gpio_test->inth); + uint8_t out; + gpio_read(gpio, GPIO_OUT_LED, &out); + gpio_write(gpio, GPIO_OUT_LED, !out); + sleep(1); + } +} + +// test thread that cycles all of the GPIOs provided to us +static int gpio_interrupt_test(void *arg) { + gpio_test_t* gpio_test = arg; + gpio_protocol_t* gpio = &gpio_test->gpio; + zx_status_t status; + + if (ZX_OK != (status = gpio_config(gpio, GPIO_OUT_LED, GPIO_DIR_OUT))) { + zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u\n", GPIO_LED); + return -1; + } + if (gpio_get_interrupt(gpio, GPIO_BUTTON, + ZX_INTERRUPT_MODE_EDGE_HIGH, &gpio_test->inth) != ZX_OK) { + zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u\n", GPIO_BUTTON); + return -1; + } + + if (gpio_config(gpio, GPIO_BUTTON, GPIO_DIR_IN | GPIO_PULL_DOWN) != ZX_OK) { + zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u status %d\n", GPIO_BUTTON, status); + return -1; + } + + thrd_create_with_name(&gpio_test->wait, gpio_waiting_thread, gpio_test, "gpio_waiting_thread"); + return 0; +} + static zx_status_t gpio_test_bind(void* ctx, zx_device_t* parent) { gpio_test_t* gpio_test = calloc(1, sizeof(gpio_test_t)); if (!gpio_test) { @@ -107,6 +164,7 @@ } thrd_create_with_name(&gpio_test->thread, gpio_test_thread, gpio_test, "gpio_test_thread"); + //thrd_create_with_name(&gpio_test->thread, gpio_interrupt_test, gpio_test, "gpio_interrupt_test"); return ZX_OK; }
diff --git a/system/dev/soc/amlogic/include/soc/aml-s912/s912-gpio.h b/system/dev/soc/amlogic/include/soc/aml-s912/s912-gpio.h index f5b6905..78cddf9 100644 --- a/system/dev/soc/amlogic/include/soc/aml-s912/s912-gpio.h +++ b/system/dev/soc/amlogic/include/soc/aml-s912/s912-gpio.h
@@ -6,7 +6,7 @@ #define S912_GPIOX_PINS 19 #define S912_GPIODV_PINS 30 -#define S912_GPIOH_PINS 32 +#define S912_GPIOH_PINS 10 #define S912_GPIOBOOT_PINS 16 #define S912_GPIOCARD_PINS 7 #define S912_GPIOCLK_PINS 2 @@ -64,6 +64,27 @@ #define S912_PERIPHS_PIN_MUX_8 0x34 #define S912_PERIPHS_PIN_MUX_9 0x35 +#define S912_PULL_UP_REG0 0x3A +#define S912_PULL_UP_REG1 0x3B +#define S912_PULL_UP_REG2 0x3C +#define S912_PULL_UP_REG3 0x3D +#define S912_PULL_UP_REG4 0x3E + +#define S912_PULL_UP_EN_REG0 0x48 +#define S912_PULL_UP_EN_REG1 0x49 +#define S912_PULL_UP_EN_REG2 0x4A +#define S912_PULL_UP_EN_REG3 0x4B +#define S912_PULL_UP_EN_REG4 0x4C + +#define S912_GPIOA0_PIN_START 0 +#define S912_GPIOZ_PIN_START 10 +#define S912_GPIOH_PIN_START 26 +#define S912_GPIOBOOT_PIN_START 36 +#define S912_GPIOCARD_PIN_START 52 +#define S912_GPIODV_PIN_START 59 +#define S912_GPIOX_PIN_START 89 +#define S912_GPIOCLK_PIN_START 108 + // GPIO AO registers live in a seperate register bank. #define S912_AO_RTI_PIN_MUX_REG 0x05 #define S912_AO_RTI_PIN_MUX_REG2 0x06
diff --git a/system/dev/soc/amlogic/include/soc/aml-s912/s912-hw.h b/system/dev/soc/amlogic/include/soc/aml-s912/s912-hw.h index 3271903..f32d6f4 100644 --- a/system/dev/soc/amlogic/include/soc/aml-s912/s912-hw.h +++ b/system/dev/soc/amlogic/include/soc/aml-s912/s912-hw.h
@@ -26,6 +26,8 @@ #define S912_GPIO_LENGTH 0x1C00 #define S912_GPIO_A0_BASE 0xc8100000 #define S912_GPIO_AO_LENGTH 0x1000 +#define S912_GPIO_INTERRUPT_BASE 0xC1100000 +#define S912_GPIO_INTERRUPT_LENGTH 0x10000 #define S912_I2C_A_BASE 0xc1108500 #define S912_I2C_A_LENGTH 0x20 @@ -47,7 +49,7 @@ #define S912_UART_A_BASE 0xc11084c0 #define S912_UART_A_LENGTH 0x18 #define S912_UART_AO_B_BASE 0xc81004e0 -#define S912_UART_AO_B_LENGTH 0x18 +#define S912_UART_AO_B_LENGTH 0x18 // IRQs #define S912_M_I2C_0_IRQ 53
diff --git a/system/ulib/ddk/include/ddk/protocol/gpio.h b/system/ulib/ddk/include/ddk/protocol/gpio.h index 70e08b3..c192bdb 100644 --- a/system/ulib/ddk/include/ddk/protocol/gpio.h +++ b/system/ulib/ddk/include/ddk/protocol/gpio.h
@@ -26,6 +26,11 @@ // for level triggered GPIO_TRIGGER_HIGH = 1 << 2, GPIO_TRIGGER_LOW = 1 << 3, + + // for pull-up/pull-down + GPIO_PULL_DOWN = 0 << 4, + GPIO_PULL_UP = 1 << 4, + GPIO_PULL_MASK = 1 << 4, }; // In the functions below, the GPIO index is relative to the list of GPIOs for the device. @@ -38,6 +43,7 @@ zx_status_t (*set_alt_function)(void* ctx, uint32_t index, uint64_t function); zx_status_t (*read)(void* ctx, uint32_t index, uint8_t* out_value); zx_status_t (*write)(void* ctx, uint32_t index, uint8_t value); + zx_status_t (*get_interrupt)(void *ctx, uint32_t pin, uint32_t flags, zx_handle_t *out_handle); } gpio_protocol_ops_t; typedef struct { @@ -68,4 +74,9 @@ return gpio->ops->write(gpio->ctx, index, value); } +// gets an interrupt object pertaining to a particular GPIO pin +static inline zx_status_t gpio_get_interrupt(gpio_protocol_t* gpio, uint32_t index, + uint32_t flags, zx_handle_t *out_handle) { + return gpio->ops->get_interrupt(gpio->ctx, index, flags, out_handle); +} __END_CDECLS;
diff --git a/system/ulib/ddk/include/ddk/protocol/platform-device.h b/system/ulib/ddk/include/ddk/protocol/platform-device.h index fd1c689..787e814 100644 --- a/system/ulib/ddk/include/ddk/protocol/platform-device.h +++ b/system/ulib/ddk/include/ddk/protocol/platform-device.h
@@ -34,7 +34,7 @@ typedef struct { zx_status_t (*map_mmio)(void* ctx, uint32_t index, uint32_t cache_policy, void** out_vaddr, size_t* out_size, zx_handle_t* out_handle); - zx_status_t (*map_interrupt)(void* ctx, uint32_t index, zx_handle_t* out_handle); + zx_status_t (*map_interrupt)(void* ctx, uint32_t index, uint32_t flags, zx_handle_t* out_handle); zx_status_t (*get_bti)(void* ctx, uint32_t index, zx_handle_t* out_handle); zx_status_t (*get_device_info)(void* ctx, pdev_device_info_t* out_info); } platform_device_protocol_ops_t; @@ -54,7 +54,14 @@ // Returns an interrupt handle. "index" is relative to the list of IRQs for the device. static inline zx_status_t pdev_map_interrupt(platform_device_protocol_t* pdev, uint32_t index, zx_handle_t* out_handle) { - return pdev->ops->map_interrupt(pdev->ctx, index, out_handle); + return pdev->ops->map_interrupt(pdev->ctx, index, 0, out_handle); +} + +// Returns an interrupt handle. "index" is relative to the list of IRQs for the device. +// This API allows user to specify the mode +static inline zx_status_t pdev_get_interrupt(platform_device_protocol_t* pdev, uint32_t index, + uint32_t flags, zx_handle_t* out_handle) { + return pdev->ops->map_interrupt(pdev->ctx, index, flags, out_handle); } // Returns an IOMMU bus transaction initiator handle.