[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.