[gpio] Refactor the GPIO protocol

This is a first step in a move toward using protocols to represent
individual resources. A similar change will be made to the I2C protocol
to bring it in line with how I2C is used on x86 platforms.

From the client's point of view, an instance of the ZX_PROTOCOL_GPIO
protocol now represents a single pin. The protocol remains the same,
except the "uint32_t index" has been removed from all the protocol functions.

For devices that have only one GPIO resource assigned to them, the driver
can simply call device_get_protocol() to access the GPIO protocol for the pin.
To for devices with more than one GPIO pins, a new API in the platform bus called
pdev_get_protocol() must be used instead.

In addition, we add a new protocol ZX_PROTOCOL_GPIO_IMPL, which is now
implemented by the GPIO drivers. This protocol is essentially the same as
the old GPIO protocol, except "index" has been renamed "pin".
Board drivers may use this protocol directly when doing low level system
configuration, specifying pin numbers directly.

TEST: Booted on VIM2 and Hikey.
On VIM2, USB, display, ethernet and the GPIO test driver are working properly
On Hikey, the system boots and USB is functional.

Change-Id: I44f1bc11ad9793543361a2d19d7a2de4458c334b
diff --git a/system/dev/block/aml-sd-emmc/aml-sd-emmc.c b/system/dev/block/aml-sd-emmc/aml-sd-emmc.c
index 478c5ef..9001dba 100644
--- a/system/dev/block/aml-sd-emmc/aml-sd-emmc.c
+++ b/system/dev/block/aml-sd-emmc/aml-sd-emmc.c
@@ -482,9 +482,9 @@
 static void aml_sd_emmc_hw_reset(void* ctx) {
     aml_sd_emmc_t* dev = (aml_sd_emmc_t*)ctx;
     mtx_lock(&dev->mtx);
-    gpio_config_out(&dev->gpio, 0, 0);
+    gpio_config_out(&dev->gpio, 0);
     usleep(10 * 1000);
-    gpio_write(&dev->gpio, 0, 1);
+    gpio_write(&dev->gpio, 1);
     usleep(10 * 1000);
     aml_sd_emmc_init_regs(dev);
     mtx_unlock(&dev->mtx);
diff --git a/system/dev/block/imx-sdhci/imx-sdhci.c b/system/dev/block/imx-sdhci/imx-sdhci.c
index 53bc8f3..7368983 100644
--- a/system/dev/block/imx-sdhci/imx-sdhci.c
+++ b/system/dev/block/imx-sdhci/imx-sdhci.c
@@ -945,9 +945,9 @@
 
     mtx_lock(&dev->mtx);
 
-    gpio_write(&dev->gpio, 0, 0);
+    gpio_write(&dev->gpio, 0);
     usleep(10000);
-    gpio_write(&dev->gpio, 0, 1);
+    gpio_write(&dev->gpio, 1);
 
     dev->info.caps |= SDMMC_HOST_CAP_AUTO_CMD12;
 
@@ -1165,7 +1165,7 @@
     dev->base_clock = IMX8M_SDHCI_BASE_CLOCK; // TODO: Better way of obtaining this info
 
     // Toggle the reset button
-    if (gpio_config_out(&dev->gpio, 0, 0) != ZX_OK) {
+    if (gpio_config_out(&dev->gpio, 0) != ZX_OK) {
         SDHCI_ERROR("Could not configure RESET pin as output\n");
         goto fail;
     }
diff --git a/system/dev/board/astro/astro-audio.c b/system/dev/board/astro/astro-audio.c
index 6216c21..82b9b9b 100644
--- a/system/dev/board/astro/astro-audio.c
+++ b/system/dev/board/astro/astro-audio.c
@@ -121,16 +121,16 @@
     s905d2_pll_ena(&hifi_pll);
 
     // TDM pin assignments
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(1), S905D2_GPIOA_1_TDMB_SCLK_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(2), S905D2_GPIOA_2_TDMB_FS_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(3), S905D2_GPIOA_3_TDMB_D0_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(6), S905D2_GPIOA_6_TDMB_DIN3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(1), S905D2_GPIOA_1_TDMB_SCLK_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(2), S905D2_GPIOA_2_TDMB_FS_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(3), S905D2_GPIOA_3_TDMB_D0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(6), S905D2_GPIOA_6_TDMB_DIN3_FN);
 
     // PDM pin assignments
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(7), S905D2_GPIOA_7_PDM_DCLK_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(8), S905D2_GPIOA_8_PDM_DIN0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(7), S905D2_GPIOA_7_PDM_DCLK_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(8), S905D2_GPIOA_8_PDM_DIN0_FN);
 
-    gpio_config_out(&bus->gpio, S905D2_GPIOA(5), 1);
+    gpio_impl_config_out(&bus->gpio, S905D2_GPIOA(5), 1);
 
     status = pbus_device_add(&bus->pbus, &aml_tdm_dev);
     if (status != ZX_OK) {
diff --git a/system/dev/board/astro/astro-bluetooth.c b/system/dev/board/astro/astro-bluetooth.c
index d4799f1..8bd3345 100644
--- a/system/dev/board/astro/astro-bluetooth.c
+++ b/system/dev/board/astro/astro-bluetooth.c
@@ -7,7 +7,7 @@
 #include <ddk/device.h>
 #include <ddk/io-buffer.h>
 #include <ddk/metadata.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/serial.h>
@@ -64,7 +64,7 @@
 // Enables and configures PWM_E on the SOC_WIFI_LPO_32k768 line for the Wifi/Bluetooth module
 static zx_status_t aml_enable_wifi_32K(aml_bus_t* bus) {
     // Configure SOC_WIFI_LPO_32k768 pin for PWM_E
-    zx_status_t status = gpio_set_alt_function(&bus->gpio, SOC_WIFI_LPO_32k768, 1);
+    zx_status_t status = gpio_impl_set_alt_function(&bus->gpio, SOC_WIFI_LPO_32k768, 1);
     if (status != ZX_OK) return status;
 
     zx_handle_t bti;
@@ -100,13 +100,13 @@
     zx_status_t status;
 
     // set alternate functions to enable Bluetooth UART
-    status = gpio_set_alt_function(&bus->gpio, S905D2_UART_TX_A, S905D2_UART_TX_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_TX_A, S905D2_UART_TX_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_UART_RX_A, S905D2_UART_RX_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RX_A, S905D2_UART_RX_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_UART_CTS_A, S905D2_UART_CTS_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_CTS_A, S905D2_UART_CTS_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_UART_RTS_A, S905D2_UART_RTS_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_UART_RTS_A, S905D2_UART_RTS_A_FN);
     if (status != ZX_OK) return status;
 
     // Configure the SOC_WIFI_LPO_32k768 PWM, which is needed for the Bluetooth module to work properly
@@ -116,9 +116,9 @@
     }
 
     // set GPIO to reset Bluetooth module
-    gpio_config_out(&bus->gpio, SOC_BT_REG_ON, 0);
+    gpio_impl_config_out(&bus->gpio, SOC_BT_REG_ON, 0);
     usleep(10 * 1000);
-    gpio_write(&bus->gpio, SOC_BT_REG_ON, 1);
+    gpio_impl_write(&bus->gpio, SOC_BT_REG_ON, 1);
     usleep(100 * 1000);
 
     // Bind UART for Bluetooth HCI
diff --git a/system/dev/board/astro/astro-gpio.c b/system/dev/board/astro/astro-gpio.c
index e77717f..580402c 100644
--- a/system/dev/board/astro/astro-gpio.c
+++ b/system/dev/board/astro/astro-gpio.c
@@ -81,13 +81,13 @@
 };
 
 zx_status_t aml_gpio_init(aml_bus_t* bus) {
-    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO, &gpio_dev);
+    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio_dev);
     if (status != ZX_OK) {
         zxlogf(ERROR, "aml_gpio_init: pbus_protocol_device_add failed: %d\n", status);
         return status;
     }
 
-    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO, &bus->gpio);
+    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO_IMPL, &bus->gpio);
     if (status != ZX_OK) {
         zxlogf(ERROR, "aml_gpio_init: device_get_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/astro/astro-i2c.c b/system/dev/board/astro/astro-i2c.c
index 54dbf14..c6a4e6b 100644
--- a/system/dev/board/astro/astro-i2c.c
+++ b/system/dev/board/astro/astro-i2c.c
@@ -57,14 +57,14 @@
     // setup pinmux for our I2C busses
 
     //i2c_ao_0
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOAO(2), 1);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOAO(3), 1);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOAO(2), 1);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOAO(3), 1);
     //i2c2
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOZ(14), 3);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOZ(15), 3);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOZ(14), 3);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOZ(15), 3);
     //i2c3
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(14), 2);
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOA(15), 2);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(14), 2);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOA(15), 2);
 
     zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_I2C_IMPL, &i2c_dev);
     if (status != ZX_OK) {
diff --git a/system/dev/board/astro/astro-rawnand.c b/system/dev/board/astro/astro-rawnand.c
index 0562323..d339f01 100644
--- a/system/dev/board/astro/astro-rawnand.c
+++ b/system/dev/board/astro/astro-rawnand.c
@@ -95,25 +95,25 @@
     zx_status_t status;
 
     // Set alternate functions to enable raw_nand.
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(8), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(8), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(9), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(9), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(10), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(10), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(11), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(11), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(12), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(12), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(14), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(14), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(15), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOBOOT(15), 2);
     if (status != ZX_OK)
         return status;
 
diff --git a/system/dev/board/astro/astro-sdio.c b/system/dev/board/astro/astro-sdio.c
index 184efb5..e44ed6b 100644
--- a/system/dev/board/astro/astro-sdio.c
+++ b/system/dev/board/astro/astro-sdio.c
@@ -123,13 +123,14 @@
     zx_status_t status;
 
     // set alternate functions to enable EMMC
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D0, S905D2_WIFI_SDIO_D0_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D1, S905D2_WIFI_SDIO_D1_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D2, S905D2_WIFI_SDIO_D2_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D3, S905D2_WIFI_SDIO_D3_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_CLK, S905D2_WIFI_SDIO_CLK_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_CMD, S905D2_WIFI_SDIO_CMD_FN);
-    gpio_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_WAKE_HOST, S905D2_WIFI_SDIO_WAKE_HOST_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D0, S905D2_WIFI_SDIO_D0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D1, S905D2_WIFI_SDIO_D1_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D2, S905D2_WIFI_SDIO_D2_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_D3, S905D2_WIFI_SDIO_D3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_CLK, S905D2_WIFI_SDIO_CLK_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_CMD, S905D2_WIFI_SDIO_CMD_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_WIFI_SDIO_WAKE_HOST,
+                               S905D2_WIFI_SDIO_WAKE_HOST_FN);
     if ((status = pbus_device_add(&bus->pbus, &aml_sd_emmc_dev)) != ZX_OK) {
         zxlogf(ERROR, "aml_sdio_init could not add aml_sd_emmc_dev: %d\n", status);
         return status;
diff --git a/system/dev/board/astro/astro-thermal.c b/system/dev/board/astro/astro-thermal.c
index edc0d56..7642814 100644
--- a/system/dev/board/astro/astro-thermal.c
+++ b/system/dev/board/astro/astro-thermal.c
@@ -263,15 +263,16 @@
 zx_status_t aml_thermal_init(aml_bus_t* bus) {
     // Configure the GPIO to be Output & set it to alternate
     // function 3 which puts in PWM_D mode.
-    zx_status_t status = gpio_config_out(&bus->gpio, S905D2_PWM_D, 0);
+    zx_status_t status = gpio_impl_config_out(&bus->gpio, S905D2_PWM_D, 0);
     if (status != ZX_OK) {
-        zxlogf(ERROR, "aml_thermal_init: gpio_config failed: %d\n", status);
+        zxlogf(ERROR, "aml_thermal_init: gpio_impl_config_outgpio_impl_config_out failed: %d\n",
+               status);
         return status;
     }
 
-    status = gpio_set_alt_function(&bus->gpio, S905D2_PWM_D, S905D2_PWM_D_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S905D2_PWM_D, S905D2_PWM_D_FN);
     if (status != ZX_OK) {
-        zxlogf(ERROR, "aml_thermal_init: gpio_set_alt_function failed: %d\n", status);
+        zxlogf(ERROR, "aml_thermal_init: gpio_impl_set_alt_function failed: %d\n", status);
         return status;
     }
 
diff --git a/system/dev/board/astro/astro-touch.c b/system/dev/board/astro/astro-touch.c
index 89e1bed..2cf66f4 100644
--- a/system/dev/board/astro/astro-touch.c
+++ b/system/dev/board/astro/astro-touch.c
@@ -45,8 +45,8 @@
 zx_status_t astro_touch_init(aml_bus_t* bus) {
 
     //Check the display ID pin to determine which driver device to add
-    gpio_set_alt_function(&bus->gpio, S905D2_GPIOH(5), 0);
-    gpio_config_in(&bus->gpio, S905D2_GPIOH(5), GPIO_NO_PULL);
+    gpio_impl_set_alt_function(&bus->gpio, S905D2_GPIOH(5), 0);
+    gpio_impl_config_in(&bus->gpio, S905D2_GPIOH(5), GPIO_NO_PULL);
     uint8_t gpio_state;
     /* Two variants of display are supported, one with BOE display panel and
           ft3x27 touch controller, the other with INX panel and Goodix touch
@@ -54,7 +54,7 @@
           Logic 0 for BOE/ft3x27 combination
           Logic 1 for Innolux/Goodix combination
     */
-    gpio_read(&bus->gpio, S905D2_GPIOH(5), &gpio_state);
+    gpio_impl_read(&bus->gpio, S905D2_GPIOH(5), &gpio_state);
     if (gpio_state) {
         zxlogf(INFO, "Innolux/Goodix screen not supported at this time\n");
         return ZX_OK;
diff --git a/system/dev/board/astro/astro.c b/system/dev/board/astro/astro.c
index e096978..c6ab1b5 100644
--- a/system/dev/board/astro/astro.c
+++ b/system/dev/board/astro/astro.c
@@ -48,12 +48,12 @@
 static uint32_t astro_get_board_rev(aml_bus_t* bus) {
     uint32_t board_rev;
     uint8_t id0, id1, id2;
-    gpio_config_in(&bus->gpio, GPIO_HW_ID0, GPIO_NO_PULL);
-    gpio_config_in(&bus->gpio, GPIO_HW_ID1, GPIO_NO_PULL);
-    gpio_config_in(&bus->gpio, GPIO_HW_ID2, GPIO_NO_PULL);
-    gpio_read(&bus->gpio, GPIO_HW_ID0, &id0);
-    gpio_read(&bus->gpio, GPIO_HW_ID1, &id1);
-    gpio_read(&bus->gpio, GPIO_HW_ID2, &id2);
+    gpio_impl_config_in(&bus->gpio, GPIO_HW_ID0, GPIO_NO_PULL);
+    gpio_impl_config_in(&bus->gpio, GPIO_HW_ID1, GPIO_NO_PULL);
+    gpio_impl_config_in(&bus->gpio, GPIO_HW_ID2, GPIO_NO_PULL);
+    gpio_impl_read(&bus->gpio, GPIO_HW_ID0, &id0);
+    gpio_impl_read(&bus->gpio, GPIO_HW_ID1, &id1);
+    gpio_impl_read(&bus->gpio, GPIO_HW_ID2, &id2);
     board_rev = id0 + (id1 << 1) + (id2 << 2);
 
     if (board_rev >= MAX_SUPPORTED_REV) {
diff --git a/system/dev/board/astro/astro.h b/system/dev/board/astro/astro.h
index f72fd72..c54e3dc 100644
--- a/system/dev/board/astro/astro.h
+++ b/system/dev/board/astro/astro.h
@@ -6,7 +6,7 @@
 
 #include <ddk/device.h>
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/iommu.h>
 #include <ddk/protocol/platform-bus.h>
 #include <soc/aml-s905d2/s905d2-gpio.h>
@@ -29,7 +29,7 @@
 typedef struct {
     zx_device_t* parent;
     platform_bus_protocol_t pbus;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     iommu_protocol_t iommu;
 } aml_bus_t;
 
diff --git a/system/dev/board/gauss/gauss-gpio.c b/system/dev/board/gauss/gauss-gpio.c
index 3b61bc9..817eb2d 100644
--- a/system/dev/board/gauss/gauss-gpio.c
+++ b/system/dev/board/gauss/gauss-gpio.c
@@ -73,11 +73,11 @@
 };
 
 zx_status_t gauss_gpio_init(gauss_bus_t* bus) {
-    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO, &gpio_dev);
+    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio_dev);
     if (status != ZX_OK) {
         zxlogf(ERROR, "gauss_gpio_init: pbus_protocol_device_add failed: %d\n", status);
         return status;
     }
 
-    return device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO, &bus->gpio);
+    return device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO_IMPL, &bus->gpio);
 }
diff --git a/system/dev/board/gauss/gauss-rawnand.c b/system/dev/board/gauss/gauss-rawnand.c
index 64ce8e8..710e76e 100644
--- a/system/dev/board/gauss/gauss-rawnand.c
+++ b/system/dev/board/gauss/gauss-rawnand.c
@@ -99,22 +99,22 @@
     zx_status_t status;
 
     // set alternate functions to enable raw_nand
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(8), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(8), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(9), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(9), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(10), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(10), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(11), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(11), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(12), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(12), 2);
     if (status != ZX_OK)
         return status;
-    status = gpio_set_alt_function(&bus->gpio, A113_GPIOBOOT(13), 2);
+    status = gpio_impl_set_alt_function(&bus->gpio, A113_GPIOBOOT(13), 2);
     if (status != ZX_OK)
         return status;
 
diff --git a/system/dev/board/gauss/gauss.c b/system/dev/board/gauss/gauss.c
index a3ef80e..6fa41ce 100644
--- a/system/dev/board/gauss/gauss.c
+++ b/system/dev/board/gauss/gauss.c
@@ -114,25 +114,25 @@
     }
 
     // pinmux for Gauss i2c
-    gpio_set_alt_function(&bus->gpio, I2C_SCK_A, 1);
-    gpio_set_alt_function(&bus->gpio, I2C_SDA_A, 1);
-    gpio_set_alt_function(&bus->gpio, I2C_SCK_B, 1);
-    gpio_set_alt_function(&bus->gpio, I2C_SDA_B, 1);
+    gpio_impl_set_alt_function(&bus->gpio, I2C_SCK_A, 1);
+    gpio_impl_set_alt_function(&bus->gpio, I2C_SDA_A, 1);
+    gpio_impl_set_alt_function(&bus->gpio, I2C_SCK_B, 1);
+    gpio_impl_set_alt_function(&bus->gpio, I2C_SDA_B, 1);
 
     // Config pinmux for gauss PDM pins
-    gpio_set_alt_function(&bus->gpio, A113_GPIOA(14), 1);
-    gpio_set_alt_function(&bus->gpio, A113_GPIOA(15), 1);
-    gpio_set_alt_function(&bus->gpio, A113_GPIOA(16), 1);
-    gpio_set_alt_function(&bus->gpio, A113_GPIOA(17), 1);
-    gpio_set_alt_function(&bus->gpio, A113_GPIOA(18), 1);
+    gpio_impl_set_alt_function(&bus->gpio, A113_GPIOA(14), 1);
+    gpio_impl_set_alt_function(&bus->gpio, A113_GPIOA(15), 1);
+    gpio_impl_set_alt_function(&bus->gpio, A113_GPIOA(16), 1);
+    gpio_impl_set_alt_function(&bus->gpio, A113_GPIOA(17), 1);
+    gpio_impl_set_alt_function(&bus->gpio, A113_GPIOA(18), 1);
 
-    gpio_set_alt_function(&bus->gpio, TDM_BCLK_C, 1);
-    gpio_set_alt_function(&bus->gpio, TDM_FSYNC_C, 1);
-    gpio_set_alt_function(&bus->gpio, TDM_MOSI_C, 1);
-    gpio_set_alt_function(&bus->gpio, TDM_MISO_C, 2);
+    gpio_impl_set_alt_function(&bus->gpio, TDM_BCLK_C, 1);
+    gpio_impl_set_alt_function(&bus->gpio, TDM_FSYNC_C, 1);
+    gpio_impl_set_alt_function(&bus->gpio, TDM_MOSI_C, 1);
+    gpio_impl_set_alt_function(&bus->gpio, TDM_MISO_C, 2);
 
-    gpio_set_alt_function(&bus->gpio, SPK_MUTEn, 0);
-    gpio_config_out(&bus->gpio, SPK_MUTEn, 1);
+    gpio_impl_set_alt_function(&bus->gpio, SPK_MUTEn, 0);
+    gpio_impl_config_out(&bus->gpio, SPK_MUTEn, 1);
 
     if ((status = gauss_i2c_init(bus)) != ZX_OK) {
         zxlogf(ERROR, "gauss_i2c_init failed: %d\n", status);
diff --git a/system/dev/board/gauss/gauss.h b/system/dev/board/gauss/gauss.h
index 6abc9b1..c944bc0 100644
--- a/system/dev/board/gauss/gauss.h
+++ b/system/dev/board/gauss/gauss.h
@@ -5,7 +5,7 @@
 #pragma once
 
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/iommu.h>
 #include <ddk/protocol/platform-bus.h>
 #include <soc/aml-a113/a113-clocks.h>
@@ -31,7 +31,7 @@
 typedef struct {
     zx_device_t* parent;
     platform_bus_protocol_t pbus;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     iommu_protocol_t iommu;
     zx_handle_t bti_handle;
     io_buffer_t usb_phy;
diff --git a/system/dev/board/imx8mevk/imx8mevk-gpio.c b/system/dev/board/imx8mevk/imx8mevk-gpio.c
index 144c84f..4e8b9b1 100644
--- a/system/dev/board/imx8mevk/imx8mevk-gpio.c
+++ b/system/dev/board/imx8mevk/imx8mevk-gpio.c
@@ -98,13 +98,13 @@
 };
 
 zx_status_t imx8m_gpio_init(imx8mevk_bus_t* bus) {
-    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO, &gpio_dev);
+    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio_dev);
     if (status != ZX_OK) {
         zxlogf(ERROR, "%s: pbus_protocol_device_add failed %d\n", __FUNCTION__, status);
         return status;
     }
 
-    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO, &bus->gpio);
+    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO_IMPL, &bus->gpio);
     if (status != ZX_OK) {
         zxlogf(ERROR, "%s: device_get_protocol failed %d\n", __FUNCTION__, status);
         return status;
diff --git a/system/dev/board/imx8mevk/imx8mevk.c b/system/dev/board/imx8mevk/imx8mevk.c
index 11d347c..e81f27e 100644
--- a/system/dev/board/imx8mevk/imx8mevk.c
+++ b/system/dev/board/imx8mevk/imx8mevk.c
@@ -118,7 +118,7 @@
 
     // Pinmux
     for (unsigned i = 0; i < sizeof(imx8mevk_pinmux) / sizeof(imx8mevk_pinmux[0]); i++) {
-        gpio_set_alt_function(&bus->gpio, 0, imx8mevk_pinmux[i]);
+        gpio_impl_set_alt_function(&bus->gpio, 0, imx8mevk_pinmux[i]);
     }
 
     if ((status = imx_i2c_init(bus)) != ZX_OK) {
diff --git a/system/dev/board/imx8mevk/imx8mevk.h b/system/dev/board/imx8mevk/imx8mevk.h
index 11d70f4..cbb3a1f 100644
--- a/system/dev/board/imx8mevk/imx8mevk.h
+++ b/system/dev/board/imx8mevk/imx8mevk.h
@@ -6,7 +6,7 @@
 
 #include <ddk/device.h>
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/iommu.h>
 #include <ddk/protocol/platform-bus.h>
 
@@ -25,7 +25,7 @@
     platform_bus_protocol_t pbus;
     zx_device_t* parent;
     iommu_protocol_t iommu;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     zx_handle_t bti_handle;
     uint32_t soc_pid;
 } imx8mevk_bus_t;
diff --git a/system/dev/board/vim/vim-eth.c b/system/dev/board/vim/vim-eth.c
index 0ce6f22..c5a2516 100644
--- a/system/dev/board/vim/vim-eth.c
+++ b/system/dev/board/vim/vim-eth.c
@@ -89,26 +89,26 @@
 zx_status_t vim_eth_init(vim_bus_t* bus) {
 
     // setup pinmux for RGMII connections
-    gpio_set_alt_function(&bus->gpio, S912_ETH_MDIO, S912_ETH_MDIO_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_MDC, S912_ETH_MDC_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RGMII_RX_CLK,
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_MDIO, S912_ETH_MDIO_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_MDC, S912_ETH_MDC_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RGMII_RX_CLK,
                           S912_ETH_RGMII_RX_CLK_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RX_DV, S912_ETH_RX_DV_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RXD0, S912_ETH_RXD0_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RXD1, S912_ETH_RXD1_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RXD2, S912_ETH_RXD2_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RXD3, S912_ETH_RXD3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RX_DV, S912_ETH_RX_DV_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RXD0, S912_ETH_RXD0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RXD1, S912_ETH_RXD1_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RXD2, S912_ETH_RXD2_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RXD3, S912_ETH_RXD3_FN);
 
-    gpio_set_alt_function(&bus->gpio, S912_ETH_RGMII_TX_CLK,
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_RGMII_TX_CLK,
                           S912_ETH_RGMII_TX_CLK_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_TX_EN, S912_ETH_TX_EN_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_TXD0, S912_ETH_TXD0_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_TXD1, S912_ETH_TXD1_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_TXD2, S912_ETH_TXD2_FN);
-    gpio_set_alt_function(&bus->gpio, S912_ETH_TXD3, S912_ETH_TXD3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_TX_EN, S912_ETH_TX_EN_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_TXD0, S912_ETH_TXD0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_TXD1, S912_ETH_TXD1_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_TXD2, S912_ETH_TXD2_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_ETH_TXD3, S912_ETH_TXD3_FN);
 
     // Set reset line to output
-    gpio_config_out(&bus->gpio, S912_GPIOZ(14), 0);
+    gpio_impl_config_out(&bus->gpio, S912_GPIOZ(14), 0);
 
     zx_status_t status = pbus_device_add(&bus->pbus, &eth_dev);
     if (status != ZX_OK) {
diff --git a/system/dev/board/vim/vim-gpio.c b/system/dev/board/vim/vim-gpio.c
index a592866..3e097f7 100644
--- a/system/dev/board/vim/vim-gpio.c
+++ b/system/dev/board/vim/vim-gpio.c
@@ -82,13 +82,13 @@
 
 zx_status_t vim_gpio_init(vim_bus_t* bus) {
 
-    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO, &gpio_dev);
+    zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio_dev);
     if (status != ZX_OK) {
         zxlogf(ERROR, "vim_gpio_init: pbus_protocol_device_add failed: %d\n", status);
         return status;
     }
 
-    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO, &bus->gpio);
+    status = device_get_protocol(bus->parent, ZX_PROTOCOL_GPIO_IMPL, &bus->gpio);
     if (status != ZX_OK) {
         zxlogf(ERROR, "vim_gpio_init: device_get_protocol failed: %d\n", status);
         return status;
diff --git a/system/dev/board/vim/vim-i2c.c b/system/dev/board/vim/vim-i2c.c
index 51caa6b..f61a209 100644
--- a/system/dev/board/vim/vim-i2c.c
+++ b/system/dev/board/vim/vim-i2c.c
@@ -68,12 +68,12 @@
 zx_status_t vim_i2c_init(vim_bus_t* bus) {
     // setup pinmux for our I2C busses
     // I2C_A and I2C_B are exposed on the 40 pin header and I2C_C on the FPC connector
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SDA_A, S912_I2C_SDA_A_FN);
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SCK_A, S912_I2C_SCK_A_FN);
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SDA_B, S912_I2C_SDA_B_FN);
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SCK_B, S912_I2C_SCK_B_FN);
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SDA_C, S912_I2C_SDA_C_FN);
-    gpio_set_alt_function(&bus->gpio, S912_I2C_SCK_C, S912_I2C_SCK_C_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SDA_A, S912_I2C_SDA_A_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SCK_A, S912_I2C_SCK_A_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SDA_B, S912_I2C_SDA_B_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SCK_B, S912_I2C_SCK_B_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SDA_C, S912_I2C_SDA_C_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_I2C_SCK_C, S912_I2C_SCK_C_FN);
 
     zx_status_t status = pbus_protocol_device_add(&bus->pbus, ZX_PROTOCOL_I2C_IMPL, &i2c_dev);
     if (status != ZX_OK) {
diff --git a/system/dev/board/vim/vim-sd-emmc.c b/system/dev/board/vim/vim-sd-emmc.c
index 6d2dadb..ca4cbcf 100644
--- a/system/dev/board/vim/vim-sd-emmc.c
+++ b/system/dev/board/vim/vim-sd-emmc.c
@@ -114,18 +114,18 @@
     zx_status_t status;
 
     // set alternate functions to enable EMMC
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D0, S912_EMMC_NAND_D0_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D1, S912_EMMC_NAND_D1_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D2, S912_EMMC_NAND_D2_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D3, S912_EMMC_NAND_D3_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D4, S912_EMMC_NAND_D4_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D5, S912_EMMC_NAND_D5_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D6, S912_EMMC_NAND_D6_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_NAND_D7, S912_EMMC_NAND_D7_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_CLK, S912_EMMC_CLK_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_RST, S912_EMMC_RST_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_CMD, S912_EMMC_CMD_FN);
-    gpio_set_alt_function(&bus->gpio, S912_EMMC_DS, S912_EMMC_DS_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D0, S912_EMMC_NAND_D0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D1, S912_EMMC_NAND_D1_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D2, S912_EMMC_NAND_D2_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D3, S912_EMMC_NAND_D3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D4, S912_EMMC_NAND_D4_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D5, S912_EMMC_NAND_D5_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D6, S912_EMMC_NAND_D6_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_NAND_D7, S912_EMMC_NAND_D7_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_CLK, S912_EMMC_CLK_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_RST, S912_EMMC_RST_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_CMD, S912_EMMC_CMD_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_EMMC_DS, S912_EMMC_DS_FN);
 
     if ((status = pbus_device_add(&bus->pbus, &emmc_dev)) != ZX_OK) {
         zxlogf(ERROR, "vim_sd_emmc_init could not add emmc_dev: %d\n", status);
diff --git a/system/dev/board/vim/vim-sdio.c b/system/dev/board/vim/vim-sdio.c
index 00c0003..0c4ba38 100644
--- a/system/dev/board/vim/vim-sdio.c
+++ b/system/dev/board/vim/vim-sdio.c
@@ -123,13 +123,13 @@
 zx_status_t vim_sdio_init(vim_bus_t* bus) {
     zx_status_t status;
 
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D0, S912_WIFI_SDIO_D0_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D1, S912_WIFI_SDIO_D1_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D2, S912_WIFI_SDIO_D2_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D3, S912_WIFI_SDIO_D3_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_CLK, S912_WIFI_SDIO_CLK_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_CMD, S912_WIFI_SDIO_CMD_FN);
-    gpio_set_alt_function(&bus->gpio, S912_WIFI_SDIO_WAKE_HOST, S912_WIFI_SDIO_WAKE_HOST_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D0, S912_WIFI_SDIO_D0_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D1, S912_WIFI_SDIO_D1_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D2, S912_WIFI_SDIO_D2_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_D3, S912_WIFI_SDIO_D3_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_CLK, S912_WIFI_SDIO_CLK_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_CMD, S912_WIFI_SDIO_CMD_FN);
+    gpio_impl_set_alt_function(&bus->gpio, S912_WIFI_SDIO_WAKE_HOST, S912_WIFI_SDIO_WAKE_HOST_FN);
 
     if ((status = pbus_device_add(&bus->pbus, &aml_sd_emmc_dev)) != ZX_OK) {
         zxlogf(ERROR, "vim_sdio_init could not add aml_sd_emmc_dev: %d\n", status);
diff --git a/system/dev/board/vim/vim-uart.c b/system/dev/board/vim/vim-uart.c
index e47780b..4649c03 100644
--- a/system/dev/board/vim/vim-uart.c
+++ b/system/dev/board/vim/vim-uart.c
@@ -114,7 +114,7 @@
 // Enables and configures PWM_E on the WIFI_32K line for the Wifi/Bluetooth module
 static zx_status_t vim_enable_wifi_32K(vim_bus_t* bus) {
     // Configure WIFI_32K pin for PWM_E
-    zx_status_t status = gpio_set_alt_function(&bus->gpio, WIFI_32K, 1);
+    zx_status_t status = gpio_impl_set_alt_function(&bus->gpio, WIFI_32K, 1);
     if (status != ZX_OK) return status;
 
     zx_handle_t bti;
@@ -150,17 +150,17 @@
     zx_status_t status;
 
     // set alternate functions to enable UART_A and UART_AO_B
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_TX_A, S912_UART_TX_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_TX_A, S912_UART_TX_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_RX_A, S912_UART_RX_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_RX_A, S912_UART_RX_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_CTS_A, S912_UART_CTS_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_CTS_A, S912_UART_CTS_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_RTS_A, S912_UART_RTS_A_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_RTS_A, S912_UART_RTS_A_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_TX_AO_B, S912_UART_TX_AO_B_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_TX_AO_B, S912_UART_TX_AO_B_FN);
     if (status != ZX_OK) return status;
-    status = gpio_set_alt_function(&bus->gpio, S912_UART_RX_AO_B, S912_UART_RX_AO_B_FN);
+    status = gpio_impl_set_alt_function(&bus->gpio, S912_UART_RX_AO_B, S912_UART_RX_AO_B_FN);
     if (status != ZX_OK) return status;
 
     // Configure the WIFI_32K PWM, which is needed for the Bluetooth module to work properly
@@ -170,9 +170,9 @@
     }
 
     // set GPIO to reset Bluetooth module
-    gpio_config_out(&bus->gpio, BT_EN, 0);
+    gpio_impl_config_out(&bus->gpio, BT_EN, 0);
     usleep(10 * 1000);
-    gpio_write(&bus->gpio, BT_EN, 1);
+    gpio_impl_write(&bus->gpio, BT_EN, 1);
 
     // Bind UART for Bluetooth HCI
     status = pbus_device_add(&bus->pbus, &bt_uart_dev);
diff --git a/system/dev/board/vim/vim.h b/system/dev/board/vim/vim.h
index 4ce4a4e..547d77e 100644
--- a/system/dev/board/vim/vim.h
+++ b/system/dev/board/vim/vim.h
@@ -6,7 +6,7 @@
 
 #include <ddk/device.h>
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/iommu.h>
 #include <ddk/protocol/platform-bus.h>
 
@@ -25,7 +25,7 @@
 
 typedef struct {
     platform_bus_protocol_t pbus;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     zx_device_t* parent;
     iommu_protocol_t iommu;
 } vim_bus_t;
diff --git a/system/dev/bus/platform/platform-bus.cpp b/system/dev/bus/platform/platform-bus.cpp
index b229e6d..1a8ec8d 100644
--- a/system/dev/bus/platform/platform-bus.cpp
+++ b/system/dev/bus/platform/platform-bus.cpp
@@ -52,11 +52,12 @@
     fbl::AllocChecker ac;
 
     switch (proto_id) {
-    case ZX_PROTOCOL_GPIO: {
+    case ZX_PROTOCOL_GPIO_IMPL: {
         if (proxy_cb != nullptr) {
             return ZX_ERR_INVALID_ARGS;
         }
-        gpio_.reset(new (&ac) ddk::GpioProtocolProxy(static_cast<gpio_protocol_t*>(protocol)));
+        gpio_.reset(new (&ac) ddk::GpioImplProtocolProxy(
+                                                static_cast<gpio_impl_protocol_t*>(protocol)));
         if (!ac.check()) {
             return ZX_ERR_NO_MEMORY;
         }
@@ -72,7 +73,7 @@
             return status;
         }
 
-        i2c_impl_.reset(new (&ac) ddk::I2cImplProtocolProxy(proto));
+        i2c_.reset(new (&ac) ddk::I2cImplProtocolProxy(proto));
         if (!ac.check()) {
             return ZX_ERR_NO_MEMORY;
         }
@@ -218,15 +219,15 @@
         proto->ops = &pbus_proto_ops_;
         return ZX_OK;
     }
-    case ZX_PROTOCOL_GPIO:
+    case ZX_PROTOCOL_GPIO_IMPL:
         if (gpio_ != nullptr) {
-            gpio_->GetProto(static_cast<gpio_protocol_t*>(out));
+            gpio_->GetProto(static_cast<gpio_impl_protocol_t*>(out));
             return ZX_OK;
         }
         break;
     case ZX_PROTOCOL_I2C_IMPL:
-        if (i2c_impl_ != nullptr) {
-            i2c_impl_->GetProto(static_cast<i2c_impl_protocol_t*>(out));
+        if (i2c_ != nullptr) {
+            i2c_->GetProto(static_cast<i2c_impl_protocol_t*>(out));
             return ZX_OK;
         }
         break;
diff --git a/system/dev/bus/platform/platform-bus.h b/system/dev/bus/platform/platform-bus.h
index 3cb3ca1..8226c68 100644
--- a/system/dev/bus/platform/platform-bus.h
+++ b/system/dev/bus/platform/platform-bus.h
@@ -7,7 +7,7 @@
 #include <ddk/device.h>
 #include <ddktl/device.h>
 #include <ddktl/protocol/clk.h>
-#include <ddktl/protocol/gpio.h>
+#include <ddktl/protocol/gpio-impl.h>
 #include <ddktl/protocol/i2c-impl.h>
 #include <ddktl/protocol/iommu.h>
 #include <ddktl/protocol/platform-bus.h>
@@ -73,8 +73,8 @@
 
     // Protocol accessors for PlatformDevice.
     inline ddk::ClkProtocolProxy* clk() const { return clk_.get(); }
-    inline ddk::GpioProtocolProxy* gpio() const { return gpio_.get(); }
-    inline ddk::I2cImplProtocolProxy* i2c_impl() const { return i2c_impl_.get(); }
+    inline ddk::GpioImplProtocolProxy* gpio() const { return gpio_.get(); }
+    inline ddk::I2cImplProtocolProxy* i2c() const { return i2c_.get(); }
 
 private:
     // This class is a wrapper for a platform_proxy_cb_t added via pbus_register_protocol().
@@ -116,9 +116,9 @@
 
     // Protocols that are optionally provided by the board driver.
     fbl::unique_ptr<ddk::ClkProtocolProxy> clk_;
-    fbl::unique_ptr<ddk::GpioProtocolProxy> gpio_;
+    fbl::unique_ptr<ddk::GpioImplProtocolProxy> gpio_;
     fbl::unique_ptr<ddk::IommuProtocolProxy> iommu_;
-    fbl::unique_ptr<ddk::I2cImplProtocolProxy> i2c_impl_;
+    fbl::unique_ptr<ddk::I2cImplProtocolProxy> i2c_;
 
     // Completion used by WaitProtocol().
     sync_completion_t proto_completion_ __TA_GUARDED(proto_completion_mutex_);
diff --git a/system/dev/bus/platform/platform-device.cpp b/system/dev/bus/platform/platform-device.cpp
index 5cd9dd0..b9461ba 100644
--- a/system/dev/bus/platform/platform-device.cpp
+++ b/system/dev/bus/platform/platform-device.cpp
@@ -219,7 +219,8 @@
     return bus_->gpio()->ConfigIn(dr->gpio(index).gpio, flags);
 }
 
-zx_status_t PlatformDevice::RpcGpioConfigOut(const DeviceResources* dr, uint32_t index, uint8_t initial_value) {
+zx_status_t PlatformDevice::RpcGpioConfigOut(const DeviceResources* dr, uint32_t index,
+                                             uint8_t initial_value) {
     if (bus_->gpio() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
     }
@@ -305,7 +306,7 @@
 
 zx_status_t PlatformDevice::RpcI2cTransact(const DeviceResources* dr, uint32_t txid,
                                            rpc_i2c_req_t* req, zx_handle_t channel) {
-    if (bus_->i2c_impl() == nullptr) {
+    if (bus_->i2c() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     uint32_t index = req->index;
@@ -319,7 +320,7 @@
 
 zx_status_t PlatformDevice::RpcI2cGetMaxTransferSize(const DeviceResources* dr, uint32_t index,
                                                      size_t* out_size) {
-    if (bus_->i2c_impl() == nullptr) {
+    if (bus_->i2c() == nullptr) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     if (index >= dr->i2c_channel_count()) {
@@ -327,7 +328,7 @@
     }
     const pbus_i2c_channel_t& pdev_channel = dr->i2c_channel(index);
 
-    return bus_->i2c_impl()->GetMaxTransferSize(pdev_channel.bus_id, out_size);
+    return bus_->i2c()->GetMaxTransferSize(pdev_channel.bus_id, out_size);
 }
 
 zx_status_t PlatformDevice::RpcClkEnable(const DeviceResources* dr, uint32_t index) {
diff --git a/system/dev/bus/platform/platform-protocol-device.cpp b/system/dev/bus/platform/platform-protocol-device.cpp
index 6857e4a..47fd273 100644
--- a/system/dev/bus/platform/platform-protocol-device.cpp
+++ b/system/dev/bus/platform/platform-protocol-device.cpp
@@ -173,6 +173,14 @@
     return ZX_ERR_NOT_SUPPORTED;
 }
 
+zx_status_t ProtocolDevice::GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol) {
+    // Pass through to DdkGetProtocol if index is zero
+    if (index != 0) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+    return DdkGetProtocol(proto_id, out_protocol);
+}
+
 zx_status_t ProtocolDevice::DdkGetProtocol(uint32_t proto_id, void* out) {
     if (proto_id == ZX_PROTOCOL_PLATFORM_DEV) {
         auto proto = static_cast<platform_device_protocol_t*>(out);
diff --git a/system/dev/bus/platform/platform-protocol-device.h b/system/dev/bus/platform/platform-protocol-device.h
index 86520b1..bc97587 100644
--- a/system/dev/bus/platform/platform-protocol-device.h
+++ b/system/dev/bus/platform/platform-protocol-device.h
@@ -54,6 +54,7 @@
     zx_status_t GetDeviceInfo(pdev_device_info_t* out_info);
     zx_status_t GetBoardInfo(pdev_board_info_t* out_info);
     zx_status_t DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out);
+    zx_status_t GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol);
 
     // Starts the underlying devmgr device.
     zx_status_t Start();
diff --git a/system/dev/bus/platform/platform-proxy-device.cpp b/system/dev/bus/platform/platform-proxy-device.cpp
index 01530fa..15612e5 100644
--- a/system/dev/bus/platform/platform-proxy-device.cpp
+++ b/system/dev/bus/platform/platform-proxy-device.cpp
@@ -30,91 +30,97 @@
 
 namespace platform_bus {
 
-zx_status_t ProxyDevice::GpioConfigIn(void* ctx, uint32_t index, uint32_t flags) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioConfigIn(void* ctx, uint32_t flags) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_CONFIG_IN;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.flags = flags;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                              sizeof(resp));
 }
 
-zx_status_t ProxyDevice::GpioConfigOut(void* ctx, uint32_t index, uint8_t initial_value) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioConfigOut(void* ctx, uint8_t initial_value) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_CONFIG_OUT;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.value = initial_value;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                              sizeof(resp));
 }
 
-zx_status_t ProxyDevice::GpioSetAltFunction(void* ctx, uint32_t index, uint64_t function) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioSetAltFunction(void* ctx, uint64_t function) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_SET_ALT_FUNCTION;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.alt_function = function;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                              sizeof(resp));
 }
 
-zx_status_t ProxyDevice::GpioGetInterrupt(void* ctx, uint32_t index, uint32_t flags,
-                                          zx_handle_t* out_handle) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioGetInterrupt(void* ctx, uint32_t flags, zx_handle_t* out_handle) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_GET_INTERRUPT;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.flags = flags;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
                               nullptr, 0, out_handle, 1, nullptr);
 }
 
-zx_status_t ProxyDevice::GpioSetPolarity(void* ctx, uint32_t index, uint32_t polarity) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioSetPolarity(void* ctx, uint32_t polarity) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_SET_POLARITY;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.polarity = polarity;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                              sizeof(resp));
 }
 
-zx_status_t ProxyDevice::GpioReleaseInterrupt(void* ctx, uint32_t index) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioReleaseInterrupt(void* ctx) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_RELEASE_INTERRUPT;
-    req.index = index;
+    req.index = gpio_ctx->index;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                              sizeof(resp));
 }
 
-zx_status_t ProxyDevice::GpioRead(void* ctx, uint32_t index, uint8_t* out_value) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioRead(void* ctx, uint8_t* out_value) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_READ;
-    req.index = index;
+    req.index = gpio_ctx->index;
 
     auto status = thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
                                     sizeof(resp));
@@ -126,13 +132,14 @@
     return ZX_OK;
 }
 
-zx_status_t ProxyDevice::GpioWrite(void* ctx, uint32_t index, uint8_t value) {
-    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
+zx_status_t ProxyDevice::GpioWrite(void* ctx, uint8_t value) {
+    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
+    auto thiz = gpio_ctx->thiz;
     rpc_gpio_req_t req = {};
     rpc_gpio_rsp_t resp = {};
     req.header.proto_id = ZX_PROTOCOL_GPIO;
     req.header.op = GPIO_WRITE;
-    req.index = index;
+    req.index = gpio_ctx->index;
     req.value = value;
 
     return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
@@ -392,6 +399,25 @@
     return CreateChild(zxdev(), resp.device_id, proxy_, args);
 }
 
+zx_status_t ProxyDevice::GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol) {
+    // Return the GPIO protocol for the given index.
+    if (proto_id == ZX_PROTOCOL_GPIO) {
+        if (index >= gpio_ctxs_.size()) {
+            return ZX_ERR_OUT_OF_RANGE;
+        }
+        auto proto = static_cast<gpio_protocol_t*>(out_protocol);
+        proto->ops = &gpio_proto_ops_;
+        proto->ctx = &gpio_ctxs_[index];
+        return ZX_OK;
+    }
+
+    // For other protocols, fall through to DdkGetProtocol if index is zero
+    if (index != 0) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+    return DdkGetProtocol(proto_id, out_protocol);
+}
+
 zx_status_t ProxyDevice::CreateRoot(zx_device_t* parent, fbl::RefPtr<PlatformProxy> proxy) {
     fbl::AllocChecker ac;
     auto dev = fbl::make_unique_checked<ProxyDevice>(&ac,parent, ROOT_DEVICE_ID, proxy);
@@ -508,6 +534,19 @@
                irq.resource.get());
     }
 
+    uint32_t gpio_count = info.gpio_count;
+    if (gpio_count > 0) {
+        gpio_ctxs_.reset(new (&ac) GpioCtx[gpio_count], gpio_count);
+        if (!ac.check()) {
+            return ZX_ERR_NO_MEMORY;
+        }
+
+        for (uint32_t i = 0; i < info.gpio_count; i++) {
+            gpio_ctxs_[i].thiz = this;
+            gpio_ctxs_[i].index = i;
+        }
+    }
+
     return ZX_OK;
 }
 
@@ -590,14 +629,24 @@
     }
 
     // Finally, protocols provided by platform bus.
+    proto->ctx = this;
     switch (proto_id) {
     case ZX_PROTOCOL_PLATFORM_DEV: {
         proto->ops = &pdev_proto_ops_;
         break;
     }
     case ZX_PROTOCOL_GPIO: {
+        auto count = gpio_ctxs_.size();
+        if (count == 0) {
+            return ZX_ERR_NOT_SUPPORTED;
+        } else if (count > 1) {
+            zxlogf(ERROR, "%s: device has more than one GPIO\n", __func__);
+            return ZX_ERR_BAD_STATE;
+        }
+        // Return zeroth GPIO resource.
         proto->ops = &gpio_proto_ops_;
-        break;
+        proto->ctx = &gpio_ctxs_[0];
+        return ZX_OK;
     }
     case ZX_PROTOCOL_I2C: {
         proto->ops = &i2c_proto_ops_;
@@ -611,7 +660,6 @@
         return proxy_->GetProtocol(proto_id, out);;
     }
 
-    proto->ctx = this;
     return ZX_OK;
 }
 
diff --git a/system/dev/bus/platform/platform-proxy-device.h b/system/dev/bus/platform/platform-proxy-device.h
index 9a6c72d..63f5cee 100644
--- a/system/dev/bus/platform/platform-proxy-device.h
+++ b/system/dev/bus/platform/platform-proxy-device.h
@@ -11,6 +11,7 @@
 #include <ddk/protocol/i2c.h>
 #include <ddktl/device.h>
 #include <ddktl/protocol/platform-device.h>
+#include <fbl/array.h>
 #include <fbl/ref_ptr.h>
 #include <fbl/unique_ptr.h>
 #include <fbl/vector.h>
@@ -61,21 +62,22 @@
     zx_status_t GetDeviceInfo(pdev_device_info_t* out_info);
     zx_status_t GetBoardInfo(pdev_board_info_t* out_info);
     zx_status_t DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out);
+    zx_status_t GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol);
 
     // Clock protocol implementation.
     static zx_status_t ClkEnable(void* ctx, uint32_t index);
     static zx_status_t ClkDisable(void* ctx, uint32_t index);
 
     // GPIO protocol implementation.
-    static zx_status_t GpioConfigIn(void* ctx, uint32_t index, uint32_t flags);
-    static zx_status_t GpioConfigOut(void* ctx, uint32_t index, uint8_t initial_value);
-    static zx_status_t GpioSetAltFunction(void* ctx, uint32_t index, uint64_t function);
-    static zx_status_t GpioRead(void* ctx, uint32_t index, uint8_t* out_value);
-    static zx_status_t GpioWrite(void* ctx, uint32_t index, uint8_t value);
-    static zx_status_t GpioGetInterrupt(void* ctx, uint32_t index, uint32_t flags,
+    static zx_status_t GpioConfigIn(void* ctx, uint32_t flags);
+    static zx_status_t GpioConfigOut(void* ctx, uint8_t initial_value);
+    static zx_status_t GpioSetAltFunction(void* ctx, uint64_t function);
+    static zx_status_t GpioRead(void* ctx, uint8_t* out_value);
+    static zx_status_t GpioWrite(void* ctx, uint8_t value);
+    static zx_status_t GpioGetInterrupt(void* ctx, uint32_t flags,
                                         zx_handle_t* out_handle);
-    static zx_status_t GpioReleaseInterrupt(void* ctx, uint32_t index);
-    static zx_status_t GpioSetPolarity(void* ctx, uint32_t index, uint32_t polarity);
+    static zx_status_t GpioReleaseInterrupt(void* ctx);
+    static zx_status_t GpioSetPolarity(void* ctx, uint32_t polarity);
 
     // I2C protocol implementation.
     static zx_status_t I2cTransact(void* ctx, uint32_t index, i2c_op_t* ops, size_t cnt,
@@ -94,6 +96,10 @@
         uint32_t mode;
         zx::handle resource;
     };
+    struct GpioCtx {
+        ProxyDevice* thiz;
+        uint32_t index;
+    };
 
     zx_status_t InitCommon();
     zx_status_t InitRoot();
@@ -114,6 +120,9 @@
     gpio_protocol_ops_t gpio_proto_ops_;
     i2c_protocol_ops_t i2c_proto_ops_;
 
+    // Contexts for our GPIOs
+    fbl::Array<GpioCtx> gpio_ctxs_;
+
     // These fields are saved values from the device_add_args_t passed to pdev_device_add().
     // These are unused for top level devices created via pbus_device_add().
     void* ctx_ = nullptr;
diff --git a/system/dev/display/astro-display/astro-display.cpp b/system/dev/display/astro-display/astro-display.cpp
index ad36288..1b77123 100644
--- a/system/dev/display/astro-display/astro-display.cpp
+++ b/system/dev/display/astro-display/astro-display.cpp
@@ -230,8 +230,8 @@
 // This function detect the panel type based.
 void AstroDisplay::PopulatePanelType() {
     uint8_t pt;
-    if ((gpio_config_in(&gpio_, GPIO_PANEL_DETECT, GPIO_NO_PULL) == ZX_OK) &&
-        (gpio_read(&gpio_, GPIO_PANEL_DETECT, &pt) == ZX_OK)) {
+    if ((gpio_config_in(&gpio_, GPIO_NO_PULL) == ZX_OK) &&
+        (gpio_read(&gpio_, &pt) == ZX_OK)) {
         panel_type_ = pt;
         DISP_INFO("Detected panel type = %s (%d)\n",
                   panel_type_ ? "P070ACB_FT" : "TV070WSM_FT", panel_type_);
@@ -410,7 +410,7 @@
     }
 
     // Obtain GPIO Protocol for Panel reset
-    status = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio_);
+    status = pdev_get_protocol(&pdev_, ZX_PROTOCOL_GPIO, GPIO_PANEL_DETECT, &gpio_);
     if (status != ZX_OK) {
         DISP_ERROR("Could not obtain GPIO protocol\n");
         return status;
diff --git a/system/dev/display/astro-display/backlight.cpp b/system/dev/display/astro-display/backlight.cpp
index 07c4ee0..bc90b56 100644
--- a/system/dev/display/astro-display/backlight.cpp
+++ b/system/dev/display/astro-display/backlight.cpp
@@ -4,6 +4,7 @@
 
 #include "backlight.h"
 #include <ddk/debug.h>
+#include <ddk/protocol/platform-device.h>
 
 namespace astro_display {
 
@@ -29,29 +30,36 @@
 } // namespace
 
 zx_status_t Backlight::Init(zx_device_t* parent) {
+    platform_device_protocol_t pdev;
+    zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev);
+    if (status != ZX_OK) {
+        DISP_ERROR("Could not obtain platform device protocol\n");
+        return status;
+    }
+
     // Obtain I2C Protocol for backlight
-    zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_I2C, &i2c_);
+    status = device_get_protocol(parent, ZX_PROTOCOL_I2C, &i2c_);
     if (status != ZX_OK) {
         DISP_ERROR("Could not obtain I2C protocol\n");
         return status;
     }
 
     // Obtain GPIO Protocol for backlight enable
-    status = device_get_protocol(parent, ZX_PROTOCOL_GPIO, &gpio_);
+    status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, GPIO_BL, &gpio_);
     if (status != ZX_OK) {
         DISP_ERROR("Could not obtain GPIO protocol\n");
         return status;
     }
 
     // set gpio pin as output
-    gpio_config_out(&gpio_, GPIO_BL, 1);
+    gpio_config_out(&gpio_, 1);
     zx_nanosleep(zx_deadline_after(ZX_USEC(10))); // optional small delay for pin to settle
     return ZX_OK;
 }
 
 void Backlight::Enable() {
     // power on backlight
-    gpio_write(&gpio_, GPIO_BL, 1);
+    gpio_write(&gpio_, 1);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(1))); // delay to ensure backlight is powered on
 
     for (size_t i = 0; i < fbl::count_of(kBacklightInitTable); i++) {
@@ -64,7 +72,7 @@
 
 void Backlight::Disable() {
     // power off backlight
-    gpio_write(&gpio_, GPIO_BL, 0);
+    gpio_write(&gpio_, 0);
 }
 
 } // namespace astro_display
diff --git a/system/dev/display/astro-display/common.h b/system/dev/display/astro-display/common.h
index fc433d2..6083488 100644
--- a/system/dev/display/astro-display/common.h
+++ b/system/dev/display/astro-display/common.h
@@ -41,6 +41,7 @@
     GPIO_HW_ID0,
     GPIO_HW_ID1,
     GPIO_HW_ID2,
+    GPIO_COUNT,
 };
 
 // Should match display_i2cs table in board driver
diff --git a/system/dev/display/astro-display/lcd.cpp b/system/dev/display/astro-display/lcd.cpp
index f2b82e5..03c08c1 100644
--- a/system/dev/display/astro-display/lcd.cpp
+++ b/system/dev/display/astro-display/lcd.cpp
@@ -4,6 +4,7 @@
 
 #include "lcd.h"
 #include <ddk/debug.h>
+#include <ddk/protocol/platform-device.h>
 #include <ddktl/device.h>
 
 #define DELAY_CMD           (0xFF)
@@ -578,9 +579,15 @@
 }
 
 zx_status_t Lcd::Init(zx_device_t* parent) {
+    platform_device_protocol_t pdev;
+    zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev);
+    if (status != ZX_OK) {
+        DISP_ERROR("Could not obtain platform device protocol\n");
+        return status;
+    }
 
     // Obtain GPIO protocol
-    zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_GPIO, &gpio_);
+    status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, GPIO_LCD, &gpio_);
     if (status != ZX_OK) {
         DISP_ERROR("Could not obtain GPIO protocol\n");
         return status;
@@ -600,12 +607,12 @@
     }
 
     // reset LCD panel via GPIO according to vendor doc
-    gpio_config_out(&gpio_, GPIO_LCD, 1);
-    gpio_write(&gpio_, GPIO_LCD, 1);
+    gpio_config_out(&gpio_, 1);
+    gpio_write(&gpio_, 1);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(30)));
-    gpio_write(&gpio_, GPIO_LCD, 0);
+    gpio_write(&gpio_, 0);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
-    gpio_write(&gpio_, GPIO_LCD, 1);
+    gpio_write(&gpio_, 1);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(30)));
     // check status
     if (GetDisplayId() != ZX_OK) {
diff --git a/system/dev/display/hikey-display/adv7533.c b/system/dev/display/hikey-display/adv7533.c
index 45bedcb..ef41261 100644
--- a/system/dev/display/hikey-display/adv7533.c
+++ b/system/dev/display/hikey-display/adv7533.c
@@ -71,7 +71,7 @@
 }
 
 zx_status_t adv7533_init(dsi_t* dsi) {
-    gpio_protocol_t* gpio = &dsi->hdmi_gpio.gpio;
+    gpio_protocol_t* gpios = dsi->hdmi_gpio.gpios;
 
     adv7533_mainchn_read(dsi, ADV7533_REG_CHIP_REVISION, 1);
     zxlogf(INFO, "%s: HDMI Ver 0x%x\n", __FUNCTION__, dsi->write_buf[0]);
@@ -138,7 +138,7 @@
     //TODO: Use GPIO IRQ once it is implemented
     uint8_t g = 0;
     do {
-        gpio_read(gpio, GPIO_INT, &g);
+        gpio_read(&gpios[GPIO_INT], &g);
     } while(g);
 
     /* Interrupt fired. Let's see if EDID is ready to be read */
diff --git a/system/dev/display/hikey-display/dsi.c b/system/dev/display/hikey-display/dsi.c
index 1d215cb..c51d814 100644
--- a/system/dev/display/hikey-display/dsi.c
+++ b/system/dev/display/hikey-display/dsi.c
@@ -113,11 +113,11 @@
 }
 
 static void hdmi_gpio_init(dsi_t* dsi) {
-    gpio_protocol_t* gpio = &dsi->hdmi_gpio.gpio;
-    gpio_config_out(gpio, GPIO_MUX, 0);
-    gpio_config_out(gpio, GPIO_PD, 0);
-    gpio_config_in(gpio, GPIO_INT, GPIO_NO_PULL);
-    gpio_write(gpio, GPIO_MUX, 0);
+    gpio_protocol_t* gpios = dsi->hdmi_gpio.gpios;
+    gpio_config_out(&gpios[GPIO_MUX], 0);
+    gpio_config_out(&gpios[GPIO_PD], 0);
+    gpio_config_in(&gpios[GPIO_INT], GPIO_NO_PULL);
+    gpio_write(&gpios[GPIO_MUX], 0);
 }
 
 static void dsi_release(void* ctx) {
@@ -370,9 +370,11 @@
     }
 
     /* Obtain the GPIO devices */
-    if (device_get_protocol(parent, ZX_PROTOCOL_GPIO, &dsi->hdmi_gpio.gpio) != ZX_OK) {
-        zxlogf(ERROR, "%s: Could not obtain GPIO Protocol\n", __FUNCTION__);
-        goto fail;
+    for (uint32_t i = 0; i < countof(dsi->hdmi_gpio.gpios); i++) {
+        if (pdev_get_protocol(&dsi->pdev, ZX_PROTOCOL_GPIO, i, &dsi->hdmi_gpio.gpios[i]) != ZX_OK) {
+            zxlogf(ERROR, "%s: Could not obtain GPIO Protocol\n", __FUNCTION__);
+            goto fail;
+        }
     }
 
     hdmi_gpio_init(dsi);
diff --git a/system/dev/display/hikey-display/dsi.h b/system/dev/display/hikey-display/dsi.h
index 23f3c6f..69fc2cd 100644
--- a/system/dev/display/hikey-display/dsi.h
+++ b/system/dev/display/hikey-display/dsi.h
@@ -167,6 +167,7 @@
     GPIO_MUX,
     GPIO_PD,
     GPIO_INT,
+    GPIO_COUNT,
 } hdmi_gpio_if_t;
 
 typedef struct {
@@ -177,7 +178,7 @@
 
 typedef struct {
     zx_device_t* zxdev;
-    gpio_protocol_t gpio;
+    gpio_protocol_t gpios[GPIO_COUNT];
 } hdmi_gpio_t;
 
 
diff --git a/system/dev/display/vim-display/vim-display.cpp b/system/dev/display/vim-display/vim-display.cpp
index 5f42625..b3511b9 100644
--- a/system/dev/display/vim-display/vim-display.cpp
+++ b/system/dev/display/vim-display/vim-display.cpp
@@ -438,7 +438,7 @@
             thrd_join(display->main_thread, &res);
         }
 
-        gpio_release_interrupt(&display->gpio, 0);
+        gpio_release_interrupt(&display->gpio);
         io_buffer_release(&display->mmio_preset);
         io_buffer_release(&display->mmio_hdmitx);
         io_buffer_release(&display->mmio_hiu);
@@ -503,7 +503,7 @@
         }
         usleep(500000);
         uint8_t hpd;
-        status = gpio_read(&display->gpio, 0, &hpd);
+        status = gpio_read(&display->gpio, &hpd);
         if (status != ZX_OK) {
             DISP_ERROR("gpio_read failed HDMI HPD\n");
             continue;
@@ -521,7 +521,7 @@
             memset(&display->cur_display_mode, 0, sizeof(display_mode_t));
             populate_added_display_args(display, &args);
             display_added = true;
-            gpio_set_polarity(&display->gpio, 0, GPIO_POLARITY_LOW);
+            gpio_set_polarity(&display->gpio, GPIO_POLARITY_LOW);
         } else if (!hpd && display->display_attached) {
             DISP_ERROR("Display Disconnected!\n");
             hdmi_shutdown(display);
@@ -530,7 +530,7 @@
             display->display_id++;
             display->display_attached = false;
 
-            gpio_set_polarity(&display->gpio, 0, GPIO_POLARITY_HIGH);
+            gpio_set_polarity(&display->gpio, GPIO_POLARITY_HIGH);
         }
 
         if (display->dc_cb &&
@@ -719,13 +719,13 @@
         return status;
     }
 
-    status = gpio_config_in(&display->gpio, 0, GPIO_PULL_DOWN);
+    status = gpio_config_in(&display->gpio, GPIO_PULL_DOWN);
     if (status != ZX_OK) {
         DISP_ERROR("gpio_config_in failed for gpio\n");
         return status;
     }
 
-    status = gpio_get_interrupt(&display->gpio, 0, ZX_INTERRUPT_MODE_LEVEL_HIGH, &display->inth);
+    status = gpio_get_interrupt(&display->gpio, ZX_INTERRUPT_MODE_LEVEL_HIGH, &display->inth);
     if (status != ZX_OK) {
         DISP_ERROR("gpio_get_interrupt failed for gpio\n");
         return status;
diff --git a/system/dev/ethernet/aml-dwmac/aml-dwmac.cpp b/system/dev/ethernet/aml-dwmac/aml-dwmac.cpp
index 430575d..319596a 100644
--- a/system/dev/ethernet/aml-dwmac/aml-dwmac.cpp
+++ b/system/dev/ethernet/aml-dwmac/aml-dwmac.cpp
@@ -24,11 +24,6 @@
 
 namespace eth {
 
-enum {
-    PHY_RESET,
-    PHY_INTR,
-};
-
 #define MCU_I2C_REG_BOOT_EN_WOL 0x21
 #define MCU_I2C_REG_BOOT_EN_WOL_RESET_ENABLE 0x03
 
@@ -96,12 +91,14 @@
         return status;
     }
 
-    status = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio_);
-    if (status != ZX_OK) {
-        return status;
+    for (uint32_t i = 0; i < countof(gpios_); i++) {
+        status = pdev_get_protocol(&pdev_, ZX_PROTOCOL_GPIO, i, &gpios_[i]);
+        if (status != ZX_OK) {
+            return status;
+        }
     }
 
-    gpio_config_out(&gpio_, PHY_RESET, 0);
+    gpio_config_out(&gpios_[PHY_RESET], 0);
     ResetPhy();
 
     // Map amlogic peripheral control registers
@@ -154,9 +151,9 @@
 }
 
 void AmlDWMacDevice::ResetPhy() {
-    gpio_write(&gpio_, PHY_RESET, 0);
+    gpio_write(&gpios_[PHY_RESET], 0);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
-    gpio_write(&gpio_, PHY_RESET, 1);
+    gpio_write(&gpios_[PHY_RESET], 1);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
 }
 
@@ -538,7 +535,7 @@
     dwmac_regs_->conf &= ~(GMAC_CONF_TE | GMAC_CONF_RE);
 
     //reset the phy (hold in reset)
-    gpio_write(&gpio_, PHY_RESET, 0);
+    gpio_write(&gpios_[PHY_RESET], 0);
 
     //transmit and receive are not disables, safe to null descriptor list ptrs
     dwdma_regs_->txdesclistaddr = 0;
diff --git a/system/dev/ethernet/aml-dwmac/aml-dwmac.h b/system/dev/ethernet/aml-dwmac/aml-dwmac.h
index f9e4bcd..cf8a3ec 100644
--- a/system/dev/ethernet/aml-dwmac/aml-dwmac.h
+++ b/system/dev/ethernet/aml-dwmac/aml-dwmac.h
@@ -100,6 +100,12 @@
     zx_handle_t EthmacGetBti();
 
 private:
+    enum {
+        PHY_RESET,
+        PHY_INTR,
+        GPIO_COUNT,
+    };
+
     zx_status_t InitBuffers();
     zx_status_t InitDevice();
     zx_status_t DeInitDevice() __TA_REQUIRES(lock_);
@@ -156,7 +162,7 @@
     dw_mac_regs_t* dwmac_regs_ = nullptr;
     dw_dma_regs_t* dwdma_regs_ = nullptr;
 
-    gpio_protocol_t gpio_;
+    gpio_protocol_t gpios_[GPIO_COUNT];
 
     fbl::Mutex lock_;
     fbl::unique_ptr<ddk::EthmacIfcProxy> ethmac_proxy_ __TA_GUARDED(lock_);
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 4b74bce..f907eb2 100644
--- a/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c
+++ b/system/dev/gpio/aml-axg-gpio/aml-axg-gpio.c
@@ -9,7 +9,7 @@
 #include <ddk/binding.h>
 #include <ddk/debug.h>
 #include <ddk/device.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/platform-device.h>
@@ -61,7 +61,7 @@
 
 typedef struct {
     platform_device_protocol_t pdev;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     zx_device_t* zxdev;
     io_buffer_t mmios[2];    // separate MMIO for AO domain
     io_buffer_t mmio_interrupt;
@@ -413,7 +413,7 @@
     return ZX_OK;
 }
 
-static gpio_protocol_ops_t gpio_ops = {
+static gpio_impl_protocol_ops_t gpio_ops = {
     .config_in = aml_gpio_config_in,
     .config_out = aml_gpio_config_out,
     .set_alt_function = aml_gpio_set_alt_function,
@@ -521,7 +521,7 @@
     gpio->gpio_interrupt->irq_status = 0;
     gpio->gpio.ops = &gpio_ops;
     gpio->gpio.ctx = gpio;
-    pbus_register_protocol(&pbus, ZX_PROTOCOL_GPIO, &gpio->gpio, NULL, NULL);
+    pbus_register_protocol(&pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio->gpio, NULL, NULL);
     gpio->gpio_interrupt->irq_info = calloc(gpio->gpio_interrupt->irq_count,
                                      sizeof(uint8_t));
     if (!gpio->gpio_interrupt->irq_info) {
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 0bb13d9..52eb44c 100644
--- a/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c
+++ b/system/dev/gpio/aml-gxl-gpio/aml-gxl-gpio.c
@@ -9,7 +9,7 @@
 #include <ddk/binding.h>
 #include <ddk/debug.h>
 #include <ddk/device.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/platform-device.h>
@@ -71,7 +71,7 @@
 
 typedef struct {
     platform_device_protocol_t pdev;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     zx_device_t* zxdev;
     io_buffer_t mmios[2];    // separate MMIO for AO domain
     io_buffer_t mmio_interrupt;
@@ -439,7 +439,7 @@
     return ZX_OK;
 }
 
-static gpio_protocol_ops_t gpio_ops = {
+static gpio_impl_protocol_ops_t gpio_ops = {
     .config_in = aml_gpio_config_in,
     .config_out = aml_gpio_config_out,
     .set_alt_function = aml_gpio_set_alt_function,
@@ -563,7 +563,7 @@
         goto fail;
     }
 
-    pbus_register_protocol(&pbus, ZX_PROTOCOL_GPIO, &gpio->gpio, NULL, NULL);
+    pbus_register_protocol(&pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio->gpio, NULL, NULL);
 
     return ZX_OK;
 
diff --git a/system/dev/gpio/gpio-test/gpio-test.c b/system/dev/gpio/gpio-test/gpio-test.c
index 417917c..c2dedc2 100644
--- a/system/dev/gpio/gpio-test/gpio-test.c
+++ b/system/dev/gpio/gpio-test/gpio-test.c
@@ -24,7 +24,7 @@
 
 typedef struct {
     zx_device_t* zxdev;
-    gpio_protocol_t gpio;
+    gpio_protocol_t* gpios;
     uint32_t gpio_count;
     thrd_t thread;
     thrd_t wait;
@@ -40,13 +40,14 @@
 
 static void gpio_test_release(void* ctx) {
     gpio_test_t* gpio_test = ctx;
-    gpio_protocol_t* gpio = &gpio_test->gpio;
+    gpio_protocol_t* gpios = gpio_test->gpios;
 
     gpio_test->done = true;
     zx_handle_close(gpio_test->inth);
-    gpio_release_interrupt(gpio, GPIO_BUTTON);
+    gpio_release_interrupt(&gpios[GPIO_BUTTON]);
     thrd_join(gpio_test->thread, NULL);
     thrd_join(gpio_test->wait, NULL);
+    free(gpio_test->gpios);
     free(gpio_test);
 }
 
@@ -58,11 +59,11 @@
 // test thread that cycles all of the GPIOs provided to us
 static int gpio_test_thread(void *arg) {
     gpio_test_t* gpio_test = arg;
-    gpio_protocol_t* gpio = &gpio_test->gpio;
+    gpio_protocol_t* gpios = gpio_test->gpios;
     uint32_t gpio_count = gpio_test->gpio_count;
 
     for (unsigned i = 0; i < gpio_count; i++) {
-        if (gpio_config_out(gpio, i, 0) != ZX_OK) {
+        if (gpio_config_out(&gpios[i], 0) != ZX_OK) {
             zxlogf(ERROR, "gpio-test: gpio_config failed for gpio %u\n", i);
             return -1;
         }
@@ -72,9 +73,9 @@
         // Assuming here that the last GPIO is the input button
         // so we don't toggle that one
         for (unsigned i = 0; i < gpio_count-1; i++) {
-            gpio_write(gpio, i, 1);
+            gpio_write(&gpios[i], 1);
             sleep(1);
-            gpio_write(gpio, i, 0);
+            gpio_write(&gpios[i], 0);
             sleep(1);
         }
     }
@@ -84,7 +85,7 @@
 
 static int gpio_waiting_thread(void *arg) {
     gpio_test_t* gpio_test = arg;
-    gpio_protocol_t* gpio = &gpio_test->gpio;
+    gpio_protocol_t* gpios = gpio_test->gpios;
     while(1) {
         zxlogf(INFO, "Waiting for GPIO Test Input Interrupt\n");
         zx_status_t status = zx_interrupt_wait(gpio_test->inth, NULL);
@@ -94,8 +95,8 @@
         }
         zxlogf(INFO, "Received GPIO Test Input Interrupt\n");
         uint8_t out;
-        gpio_read(gpio, GPIO_LED, &out);
-        gpio_write(gpio, GPIO_LED, !out);
+        gpio_read(&gpios[GPIO_LED], &out);
+        gpio_write(&gpios[GPIO_LED], !out);
         sleep(1);
     }
 }
@@ -103,14 +104,14 @@
 // test thread that cycles runs tests for GPIO interrupts
 static int gpio_interrupt_test(void *arg) {
     gpio_test_t* gpio_test = arg;
-    gpio_protocol_t* gpio = &gpio_test->gpio;
+    gpio_protocol_t* gpios = gpio_test->gpios;
 
-    if (gpio_config_in(gpio, GPIO_BUTTON, GPIO_PULL_DOWN) != ZX_OK) {
+    if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_PULL_DOWN) != ZX_OK) {
         zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON);
         return -1;
     }
 
-    if (gpio_get_interrupt(gpio, GPIO_BUTTON,
+    if (gpio_get_interrupt(&gpios[GPIO_BUTTON],
                            ZX_INTERRUPT_MODE_EDGE_HIGH, &gpio_test->inth) != ZX_OK) {
         zxlogf(ERROR, "gpio_interrupt_test: gpio_get_interrupt failed for gpio %u\n", GPIO_BUTTON);
         return -1;
@@ -123,16 +124,16 @@
 // test thread that checks for gpio inputs
 static int gpio_test_in(void *arg) {
     gpio_test_t* gpio_test = arg;
-    gpio_protocol_t* gpio = &gpio_test->gpio;
+    gpio_protocol_t* gpios = gpio_test->gpios;
 
-    if (gpio_config_in(gpio, GPIO_BUTTON, GPIO_NO_PULL) != ZX_OK) {
+    if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_NO_PULL) != ZX_OK) {
         zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON);
         return -1;
     }
 
     uint8_t out;
     while (!gpio_test->done) {
-        gpio_read(gpio, GPIO_BUTTON, &out);
+        gpio_read(&gpios[GPIO_BUTTON], &out);
         if (out) {
             zxlogf(INFO, "READ GPIO_BUTTON %u\n",out);
             sleep(2);
@@ -147,11 +148,6 @@
         return ZX_ERR_NO_MEMORY;
     }
 
-    if (device_get_protocol(parent, ZX_PROTOCOL_GPIO, &gpio_test->gpio) != ZX_OK) {
-        free(gpio_test);
-        return ZX_ERR_NOT_SUPPORTED;
-    }
-
     platform_device_protocol_t pdev;
     if (device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev) != ZX_OK) {
         free(gpio_test);
@@ -164,6 +160,19 @@
         return ZX_ERR_NOT_SUPPORTED;
     }
     gpio_test->gpio_count = info.gpio_count;
+    gpio_test->gpios = calloc(info.gpio_count, sizeof(*gpio_test->gpios));
+    if (!gpio_test->gpios) {
+        free(gpio_test);
+        return ZX_ERR_NO_MEMORY;
+    }
+    for (uint32_t i = 0; i < info.gpio_count; i++) {
+        zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpio_test->gpios[i]);
+        if (status != ZX_OK) {
+            free(gpio_test->gpios);
+            free(gpio_test);
+            return status;
+        }
+    }
 
     device_add_args_t args = {
         .version = DEVICE_ADD_ARGS_VERSION,
diff --git a/system/dev/gpio/imx8/imx8-gpio.c b/system/dev/gpio/imx8/imx8-gpio.c
index 27f3924..7b6281e 100644
--- a/system/dev/gpio/imx8/imx8-gpio.c
+++ b/system/dev/gpio/imx8/imx8-gpio.c
@@ -9,7 +9,7 @@
 #include <ddk/binding.h>
 #include <ddk/debug.h>
 #include <ddk/device.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/platform-bus.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/platform-device.h>
@@ -25,7 +25,7 @@
 typedef struct {
     platform_device_protocol_t pdev;
     platform_bus_protocol_t pbus;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     zx_device_t* zxdev;
     io_buffer_t mmios[IMX_GPIO_BLOCKS];
     io_buffer_t mmio_iomux;
@@ -380,7 +380,7 @@
     return ZX_ERR_NOT_SUPPORTED;
 }
 
-static gpio_protocol_ops_t gpio_ops = {
+static gpio_impl_protocol_ops_t gpio_ops = {
     .config_in = imx8_gpio_config_in,
     .config_out = imx8_gpio_config_out,
     .set_alt_function = imx8_gpio_set_alt_function,
@@ -499,7 +499,7 @@
 
     gpio->gpio.ops = &gpio_ops;
     gpio->gpio.ctx = gpio;
-    pbus_register_protocol(&gpio->pbus, ZX_PROTOCOL_GPIO, &gpio->gpio, NULL, NULL);
+    pbus_register_protocol(&gpio->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio->gpio, NULL, NULL);
 
     return ZX_OK;
 
diff --git a/system/dev/gpio/pl061/include/gpio/pl061/pl061.h b/system/dev/gpio/pl061/include/gpio/pl061/pl061.h
index 09ae5db..ace2000 100644
--- a/system/dev/gpio/pl061/include/gpio/pl061/pl061.h
+++ b/system/dev/gpio/pl061/include/gpio/pl061/pl061.h
@@ -5,7 +5,7 @@
 #pragma once
 
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <ddk/protocol/platform-device.h>
 #include <zircon/listnode.h>
 #include <zircon/types.h>
@@ -22,4 +22,4 @@
 } pl061_gpios_t;
 
 // PL061 GPIO protocol ops uses pl061_gpios_t* for ctx
-extern gpio_protocol_ops_t pl061_proto_ops;
+extern gpio_impl_protocol_ops_t pl061_proto_ops;
diff --git a/system/dev/gpio/pl061/pl061.c b/system/dev/gpio/pl061/pl061.c
index 4065a63..0b2da8c 100644
--- a/system/dev/gpio/pl061/pl061.c
+++ b/system/dev/gpio/pl061/pl061.c
@@ -124,7 +124,7 @@
     return ZX_ERR_NOT_SUPPORTED;
 }
 
-gpio_protocol_ops_t pl061_proto_ops = {
+gpio_impl_protocol_ops_t pl061_proto_ops = {
     .config_in = pl061_gpio_config_in,
     .config_out = pl061_gpio_config_out,
     .set_alt_function = pl061_gpio_set_alt_function,
diff --git a/system/dev/input/focaltech/ft3x27.cpp b/system/dev/input/focaltech/ft3x27.cpp
index 3badb17..acebb78 100644
--- a/system/dev/input/focaltech/ft3x27.cpp
+++ b/system/dev/input/focaltech/ft3x27.cpp
@@ -63,20 +63,30 @@
 }
 
 zx_status_t Ft3x27Device::InitPdev() {
-    zx_status_t status = device_get_protocol(parent_, ZX_PROTOCOL_I2C, &i2c_);
+    platform_device_protocol_t pdev;
+
+    zx_status_t status = device_get_protocol(parent_, ZX_PROTOCOL_PLATFORM_DEV, &pdev);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "ft3x27: failed to acquire pdev\n");
+        return status;
+    }
+
+    status = device_get_protocol(parent_, ZX_PROTOCOL_I2C, &i2c_);
     if (status != ZX_OK) {
         zxlogf(ERROR, "ft3x27: failed to acquire i2c\n");
         return status;
     }
 
-    status = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio_);
-    if (status != ZX_OK) {
-        return status;
+    for (uint32_t i = 0; i < FT_PIN_COUNT; i++) {
+        status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpios_[i]);
+        if (status != ZX_OK) {
+            return status;
+        }
     }
 
-    gpio_config_in(&gpio_, FT_INT_PIN, GPIO_NO_PULL);
+    gpio_config_in(&gpios_[FT_INT_PIN], GPIO_NO_PULL);
 
-    status = gpio_get_interrupt(&gpio_, FT_INT_PIN,
+    status = gpio_get_interrupt(&gpios_[FT_INT_PIN],
                                 ZX_INTERRUPT_MODE_EDGE_LOW,
                                 irq_.reset_and_get_address());
     if (status != ZX_OK) {
diff --git a/system/dev/input/focaltech/ft3x27.h b/system/dev/input/focaltech/ft3x27.h
index 6c3ffb3..6b18078 100644
--- a/system/dev/input/focaltech/ft3x27.h
+++ b/system/dev/input/focaltech/ft3x27.h
@@ -21,10 +21,13 @@
 #include <zircon/compiler.h>
 #include <zircon/types.h>
 
-// clang-format off
-#define FT_INT_PIN            0
-#define FT_RESET_PIN          1
+enum {
+    FT_INT_PIN,
+    FT_RESET_PIN,
+    FT_PIN_COUNT,
+};
 
+// clang-format off
 #define FTS_REG_CURPOINT                    0x02
 #define FTS_REG_FINGER_START                0x03
 #define FTS_REG_INT_CNT                     0x8F
@@ -106,7 +109,7 @@
     ft3x27_touch_t ft_rpt_ __TA_GUARDED(proxy_lock_);
     void ParseReport(ft3x27_finger_t* rpt, uint8_t* buf);
 
-    gpio_protocol_t gpio_;
+    gpio_protocol_t gpios_[FT_PIN_COUNT];
     zx::interrupt irq_;
     i2c_protocol_t i2c_;
 
diff --git a/system/dev/lib/amlogic/include/soc/aml-common/aml-thermal.h b/system/dev/lib/amlogic/include/soc/aml-common/aml-thermal.h
index 4f3cc74..e204af0 100644
--- a/system/dev/lib/amlogic/include/soc/aml-common/aml-thermal.h
+++ b/system/dev/lib/amlogic/include/soc/aml-common/aml-thermal.h
@@ -22,13 +22,14 @@
 enum {
     FAN_CTL0,
     FAN_CTL1,
+    FAN_CTL_COUNT,
 };
 
 typedef struct {
     zx_device_t*                        zxdev;
     platform_device_protocol_t          pdev;
 
-    gpio_protocol_t                     gpio;
+    gpio_protocol_t                     gpios[FAN_CTL_COUNT];
     scpi_protocol_t                     scpi;
 
     zx_handle_t                         port;
diff --git a/system/dev/lib/hi3660/hi3660-gpios.c b/system/dev/lib/hi3660/hi3660-gpios.c
index d680e7a..b8202de 100644
--- a/system/dev/lib/hi3660/hi3660-gpios.c
+++ b/system/dev/lib/hi3660/hi3660-gpios.c
@@ -84,7 +84,7 @@
     return ZX_ERR_NOT_SUPPORTED;
 }
 
-static gpio_protocol_ops_t gpio_ops = {
+static gpio_impl_protocol_ops_t gpio_ops = {
     .config_in = hi3660_gpio_config_in,
     .config_out = hi3660_gpio_config_out,
     .set_alt_function = hi3660_gpio_set_alt_function,
diff --git a/system/dev/lib/hi3660/include/soc/hi3660/hi3660.h b/system/dev/lib/hi3660/include/soc/hi3660/hi3660.h
index e8d6d47..1e6bf98 100644
--- a/system/dev/lib/hi3660/include/soc/hi3660/hi3660.h
+++ b/system/dev/lib/hi3660/include/soc/hi3660/hi3660.h
@@ -5,12 +5,12 @@
 #pragma once
 
 #include <ddk/io-buffer.h>
-#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/gpio-impl.h>
 #include <zircon/listnode.h>
 
 typedef struct {
     list_node_t gpios;
-    gpio_protocol_t gpio;
+    gpio_impl_protocol_t gpio;
     io_buffer_t usb3otg_bc;
     io_buffer_t peri_crg;
     io_buffer_t iomcu;
diff --git a/system/dev/light/ams-light/tcs3400.cpp b/system/dev/light/ams-light/tcs3400.cpp
index e845463..9d19b8c 100644
--- a/system/dev/light/ams-light/tcs3400.cpp
+++ b/system/dev/light/ams-light/tcs3400.cpp
@@ -23,7 +23,6 @@
 #include "tcs3400-regs.h"
 #include "tcs3400.h"
 
-constexpr uint32_t TCS3400_INTERRUPT_IDX = 0;
 constexpr zx_duration_t INTERRUPTS_HYSTERESIS = ZX_MSEC(100);
 constexpr uint8_t SAMPLES_TO_TRIGGER = 0x01;
 
@@ -346,10 +345,9 @@
         return ZX_ERR_NOT_SUPPORTED;
     }
 
-    gpio_config_in(&gpio_, TCS3400_INTERRUPT_IDX, GPIO_NO_PULL);
+    gpio_config_in(&gpio_, GPIO_NO_PULL);
 
-    status = gpio_get_interrupt(&gpio_, TCS3400_INTERRUPT_IDX,
-                                ZX_INTERRUPT_MODE_EDGE_LOW,
+    status = gpio_get_interrupt(&gpio_, ZX_INTERRUPT_MODE_EDGE_LOW,
                                 irq_.reset_and_get_address());
     if (status != ZX_OK) {
         return status;
diff --git a/system/dev/pci/amlogic-pcie/aml-pcie-device.cpp b/system/dev/pci/amlogic-pcie/aml-pcie-device.cpp
index e0622de..4c1b65d 100644
--- a/system/dev/pci/amlogic-pcie/aml-pcie-device.cpp
+++ b/system/dev/pci/amlogic-pcie/aml-pcie-device.cpp
@@ -16,8 +16,6 @@
 namespace pcie {
 namespace aml {
 
-const size_t kRstGpio = 0;
-
 const size_t kElbMmio = 0;
 const size_t kCfgMmio = 1;
 const size_t kRstMmio = 2;
@@ -42,7 +40,7 @@
         return st;
     }
 
-    st = gpio_config_out(&gpio_, kRstGpio, 0);
+    st = gpio_config_out(&gpio_, 0);
     if (st != ZX_OK) {
         zxlogf(ERROR, "aml_pcie: failed to configure rst gpio, st = %d", st);
         return st;
@@ -196,9 +194,9 @@
     }
 
     // Whack the reset gpio.
-    gpio_write(&gpio_, kRstGpio, 0);
+    gpio_write(&gpio_, 0);
     zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
-    gpio_write(&gpio_, kRstGpio, 1);
+    gpio_write(&gpio_, 1);
 
     st = pcie_->EstablishLink(&atu_cfg_, &atu_io_, &atu_mem_);
     if (st != ZX_OK) {
diff --git a/system/dev/thermal/aml-thermal-s912/aml-thermal.c b/system/dev/thermal/aml-thermal-s912/aml-thermal.c
index 2fa7f4e..92f7e1a 100644
--- a/system/dev/thermal/aml-thermal-s912/aml-thermal.c
+++ b/system/dev/thermal/aml-thermal-s912/aml-thermal.c
@@ -28,20 +28,20 @@
 static void aml_set_fan_level(aml_thermal_t* dev, uint32_t level) {
     switch (level) {
     case 0:
-        gpio_write(&dev->gpio, FAN_CTL0, 0);
-        gpio_write(&dev->gpio, FAN_CTL1, 0);
+        gpio_write(&dev->gpios[FAN_CTL0], 0);
+        gpio_write(&dev->gpios[FAN_CTL1], 0);
         break;
     case 1:
-        gpio_write(&dev->gpio, FAN_CTL0, 1);
-        gpio_write(&dev->gpio, FAN_CTL1, 0);
+        gpio_write(&dev->gpios[FAN_CTL0], 1);
+        gpio_write(&dev->gpios[FAN_CTL1], 0);
         break;
     case 2:
-        gpio_write(&dev->gpio, FAN_CTL0, 0);
-        gpio_write(&dev->gpio, FAN_CTL1, 1);
+        gpio_write(&dev->gpios[FAN_CTL0], 0);
+        gpio_write(&dev->gpios[FAN_CTL1], 1);
         break;
     case 3:
-        gpio_write(&dev->gpio, FAN_CTL0, 1);
-        gpio_write(&dev->gpio, FAN_CTL1, 1);
+        gpio_write(&dev->gpios[FAN_CTL0], 1);
+        gpio_write(&dev->gpios[FAN_CTL1], 1);
         break;
     default:
         break;
@@ -293,8 +293,8 @@
     }
 
     // Configure the GPIOs
-    for (uint32_t i = 0; i < info.gpio_count; i++) {
-        status = gpio_config_out(&thermal->gpio, i, 0);
+    for (uint32_t i = 0; i < countof(thermal->gpios); i++) {
+        status = gpio_config_out(&thermal->gpios[i], 0);
         if (status != ZX_OK) {
             THERMAL_ERROR("gpio_config failed\n");
             return status;
@@ -354,10 +354,13 @@
         goto fail;
     }
 
-    status = device_get_protocol(parent, ZX_PROTOCOL_GPIO, &thermal->gpio);
-    if (status != ZX_OK) {
-        THERMAL_ERROR("Could not get GPIO protocol\n");
-        goto fail;
+    // Configure the GPIOs
+    for (uint32_t i = 0; i < countof(thermal->gpios); i++) {
+        status = pdev_get_protocol(&thermal->pdev, ZX_PROTOCOL_GPIO, i, &thermal->gpios[i]);
+        if (status != ZX_OK) {
+            THERMAL_ERROR("Could not get GPIO protocol\n");
+            goto fail;
+        }
     }
 
     status = device_get_protocol(parent, ZX_PROTOCOL_SCPI, &thermal->scpi);
diff --git a/system/dev/usb/hikey-usb/hikey-usb.cpp b/system/dev/usb/hikey-usb/hikey-usb.cpp
index 9c086ab..8b51456 100644
--- a/system/dev/usb/hikey-usb/hikey-usb.cpp
+++ b/system/dev/usb/hikey-usb/hikey-usb.cpp
@@ -14,18 +14,12 @@
 #include <ddk/debug.h>
 #include <ddk/device.h>
 #include <ddk/driver.h>
+#include <ddk/protocol/gpio.h>
 #include <ddk/protocol/platform-defs.h>
 #include <ddk/protocol/platform-device.h>
-#include <ddktl/protocol/gpio.h>
 #include <fbl/algorithm.h>
 #include <fbl/unique_ptr.h>
 
-enum {
-    HUB_VDD33_EN,
-    VBUS_TYPEC,
-    USBSW_SW_SEL,
-};
-
 namespace hikey_usb {
 
 zx_status_t HikeyUsb::Create(zx_device_t* parent) {
@@ -52,16 +46,14 @@
     if (status != ZX_OK) {
         return status;
     }
-    status = device_get_protocol(parent(), ZX_PROTOCOL_GPIO, &gpio_);
-    if (status != ZX_OK) {
-        return status;
+    for (uint32_t i = 0; i < countof(gpios_); i++) {
+        status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpios_[i]);
+        if (status != ZX_OK) {
+            return status;
+        }
+        gpio_config_out(&gpios_[i], 0);
     }
 
-    ddk::GpioProtocolProxy gpio(&gpio_);
-    gpio.ConfigOut(HUB_VDD33_EN, 0);
-    gpio.ConfigOut(VBUS_TYPEC, 0);
-    gpio.ConfigOut(USBSW_SW_SEL, 0);
-
     zx_device_prop_t props[] = {
         {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC},
         {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC},
@@ -89,10 +81,9 @@
         return ZX_ERR_NOT_SUPPORTED;
     }
 
-    ddk::GpioProtocolProxy gpio(&gpio_);
-    gpio.Write(HUB_VDD33_EN, mode == USB_MODE_HOST);
-    gpio.Write(VBUS_TYPEC, mode == USB_MODE_HOST);
-    gpio.Write(USBSW_SW_SEL, mode == USB_MODE_HOST);
+    gpio_write(&gpios_[HUB_VDD33_EN], mode == USB_MODE_HOST);
+    gpio_write(&gpios_[VBUS_TYPEC], mode == USB_MODE_HOST);
+    gpio_write(&gpios_[USBSW_SW_SEL], mode == USB_MODE_HOST);
 
     usb_mode_ = mode;
     return ZX_OK;
diff --git a/system/dev/usb/hikey-usb/hikey-usb.h b/system/dev/usb/hikey-usb/hikey-usb.h
index 4ef4fdc..76d4661 100644
--- a/system/dev/usb/hikey-usb/hikey-usb.h
+++ b/system/dev/usb/hikey-usb/hikey-usb.h
@@ -32,9 +32,16 @@
 private:
     DISALLOW_COPY_ASSIGN_AND_MOVE(HikeyUsb);
 
+    enum {
+        HUB_VDD33_EN,
+        VBUS_TYPEC,
+        USBSW_SW_SEL,
+        GPIO_COUNT,
+    };
+
     zx_status_t Init();
 
-    gpio_protocol_t gpio_;
+    gpio_protocol_t gpios_[GPIO_COUNT];
     usb_mode_t usb_mode_;
 };
 
diff --git a/system/ulib/ddk/include/ddk/protocol/gpio-impl.h b/system/ulib/ddk/include/ddk/protocol/gpio-impl.h
new file mode 100644
index 0000000..df9dc1e
--- /dev/null
+++ b/system/ulib/ddk/include/ddk/protocol/gpio-impl.h
@@ -0,0 +1,76 @@
+// Copyright 2017 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
+
+#include <ddk/protocol/gpio.h>
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS;
+
+typedef struct {
+    zx_status_t (*config_in)(void* ctx, uint32_t pin, uint32_t flags);
+    zx_status_t (*config_out)(void* ctx, uint32_t pin, uint8_t initial_value);
+    zx_status_t (*set_alt_function)(void* ctx, uint32_t pin, uint64_t function);
+    zx_status_t (*read)(void* ctx, uint32_t pin, uint8_t* out_value);
+    zx_status_t (*write)(void* ctx, uint32_t pin, uint8_t value);
+    zx_status_t (*get_interrupt)(void* ctx, uint32_t pin, uint32_t flags, zx_handle_t* out_handle);
+    zx_status_t (*release_interrupt)(void* ctx, uint32_t pin);
+    zx_status_t (*set_polarity)(void* ctx, uint32_t pin, uint32_t polarity);
+} gpio_impl_protocol_ops_t;
+
+typedef struct {
+    gpio_impl_protocol_ops_t* ops;
+    void* ctx;
+} gpio_impl_protocol_t;
+
+// configures a GPIO for input
+static inline zx_status_t gpio_impl_config_in(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                              uint32_t flags) {
+    return gpio->ops->config_in(gpio->ctx, pin, flags);
+}
+
+// configures a GPIO for output
+static inline zx_status_t gpio_impl_config_out(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                               uint8_t initial_value) {
+    return gpio->ops->config_out(gpio->ctx, pin, initial_value);
+}
+
+// configures the GPIO pin for an alternate function (I2C, SPI, etc)
+// the interpretation of "function" is platform dependent
+static inline zx_status_t gpio_impl_set_alt_function(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                                     uint64_t function) {
+    return gpio->ops->set_alt_function(gpio->ctx, pin, function);
+}
+
+// reads the current value of a GPIO (0 or 1)
+static inline zx_status_t gpio_impl_read(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                         uint8_t* out_value) {
+    return gpio->ops->read(gpio->ctx, pin, out_value);
+}
+
+// sets the current value of the GPIO (any non-zero value maps to 1)
+static inline zx_status_t gpio_impl_write(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                          uint8_t value) {
+    return gpio->ops->write(gpio->ctx, pin, value);
+}
+
+// gets an interrupt object pertaining to a particular GPIO pin
+static inline zx_status_t gpio_impl_get_interrupt(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                                  uint32_t flags, zx_handle_t* out_handle) {
+    return gpio->ops->get_interrupt(gpio->ctx, pin, flags, out_handle);
+}
+
+// release the interrupt
+static inline zx_status_t gpio_impl_release_interrupt(const gpio_impl_protocol_t* gpio, uint32_t pin) {
+    return gpio->ops->release_interrupt(gpio->ctx, pin);
+}
+
+// Set GPIO polarity
+static inline zx_status_t gpio_impl_set_polarity(const gpio_impl_protocol_t* gpio, uint32_t pin,
+                                                 uint32_t polarity) {
+    return gpio->ops->set_polarity(gpio->ctx, pin, polarity);
+}
+__END_CDECLS;
diff --git a/system/ulib/ddk/include/ddk/protocol/gpio.h b/system/ulib/ddk/include/ddk/protocol/gpio.h
index b50f6ed..7ed0847 100644
--- a/system/ulib/ddk/include/ddk/protocol/gpio.h
+++ b/system/ulib/ddk/include/ddk/protocol/gpio.h
@@ -19,20 +19,15 @@
 #define GPIO_POLARITY_LOW       0
 #define GPIO_POLARITY_HIGH      1
 
-// In the functions below, the GPIO index is relative to the list of GPIOs for the device.
-// For example, the list of GPIOs a platform device has access to would likely be a small
-// subset of the total number of GPIOs, while a platform bus implementation driver would
-// have access to the complete set of GPIOs.
-
 typedef struct {
-    zx_status_t (*config_in)(void* ctx, uint32_t index, uint32_t flags);
-    zx_status_t (*config_out)(void* ctx, uint32_t index, uint8_t initial_value);
-    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);
-    zx_status_t (*release_interrupt)(void* ctx, uint32_t pin);
-    zx_status_t (*set_polarity)(void* ctx, uint32_t pin, uint32_t polarity);
+    zx_status_t (*config_in)(void* ctx, uint32_t flags);
+    zx_status_t (*config_out)(void* ctx, uint8_t initial_value);
+    zx_status_t (*set_alt_function)(void* ctx, uint64_t function);
+    zx_status_t (*read)(void* ctx, uint8_t* out_value);
+    zx_status_t (*write)(void* ctx, uint8_t value);
+    zx_status_t (*get_interrupt)(void* ctx, uint32_t flags, zx_handle_t* out_handle);
+    zx_status_t (*release_interrupt)(void* ctx);
+    zx_status_t (*set_polarity)(void* ctx, uint32_t polarity);
 } gpio_protocol_ops_t;
 
 typedef struct {
@@ -40,50 +35,45 @@
     void* ctx;
 } gpio_protocol_t;
 
-// configures a GPIO for input
-static inline zx_status_t gpio_config_in(const gpio_protocol_t* gpio, uint32_t index,
-                                         uint32_t flags) {
-    return gpio->ops->config_in(gpio->ctx, index, flags);
+// configures the GPIO pin for input
+static inline zx_status_t gpio_config_in(const gpio_protocol_t* gpio, uint32_t flags) {
+    return gpio->ops->config_in(gpio->ctx, flags);
 }
 
-// configures a GPIO for output
-static inline zx_status_t gpio_config_out(const gpio_protocol_t* gpio, uint32_t index,
-                                          uint8_t initial_value) {
-    return gpio->ops->config_out(gpio->ctx, index, initial_value);
+// configures the GPIO pin for output
+static inline zx_status_t gpio_config_out(const gpio_protocol_t* gpio, uint8_t initial_value) {
+    return gpio->ops->config_out(gpio->ctx, initial_value);
 }
 
 // configures the GPIO pin for an alternate function (I2C, SPI, etc)
 // the interpretation of "function" is platform dependent
-static inline zx_status_t gpio_set_alt_function(const gpio_protocol_t* gpio, uint32_t index,
-                                                uint64_t function) {
-    return gpio->ops->set_alt_function(gpio->ctx, index, function);
+static inline zx_status_t gpio_set_alt_function(const gpio_protocol_t* gpio, uint64_t function) {
+    return gpio->ops->set_alt_function(gpio->ctx, function);
 }
 
-// reads the current value of a GPIO (0 or 1)
-static inline zx_status_t gpio_read(const gpio_protocol_t* gpio, uint32_t index,
-                                    uint8_t* out_value) {
-    return gpio->ops->read(gpio->ctx, index, out_value);
+// reads the current value of the GPIO pin (0 or 1)
+static inline zx_status_t gpio_read(const gpio_protocol_t* gpio, uint8_t* out_value) {
+    return gpio->ops->read(gpio->ctx, out_value);
 }
 
-// sets the current value of the GPIO (any non-zero value maps to 1)
-static inline zx_status_t gpio_write(const gpio_protocol_t* gpio, uint32_t index, uint8_t value) {
-    return gpio->ops->write(gpio->ctx, index, value);
+// sets the current value of the GPIO pin (any non-zero value maps to 1)
+static inline zx_status_t gpio_write(const gpio_protocol_t* gpio, uint8_t value) {
+    return gpio->ops->write(gpio->ctx, value);
 }
 
-// gets an interrupt object pertaining to a particular GPIO pin
-static inline zx_status_t gpio_get_interrupt(const 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);
+// gets an interrupt object pertaining to the GPIO pin
+static inline zx_status_t gpio_get_interrupt(const gpio_protocol_t* gpio, uint32_t flags,
+                                             zx_handle_t* out_handle) {
+    return gpio->ops->get_interrupt(gpio->ctx, flags, out_handle);
 }
 
 // release the interrupt
-static inline zx_status_t gpio_release_interrupt(const gpio_protocol_t* gpio, uint32_t pin) {
-    return gpio->ops->release_interrupt(gpio->ctx, pin);
+static inline zx_status_t gpio_release_interrupt(const gpio_protocol_t* gpio) {
+    return gpio->ops->release_interrupt(gpio->ctx);
 }
 
 // Set GPIO polarity
-static inline zx_status_t gpio_set_polarity(const gpio_protocol_t* gpio, uint32_t pin,
-                                            uint32_t polarity) {
-    return gpio->ops->set_polarity(gpio->ctx, pin, polarity);
+static inline zx_status_t gpio_set_polarity(const gpio_protocol_t* gpio, uint32_t polarity) {
+    return gpio->ops->set_polarity(gpio->ctx, polarity);
 }
 __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 88d2c54..0c5417e 100644
--- a/system/ulib/ddk/include/ddk/protocol/platform-device.h
+++ b/system/ulib/ddk/include/ddk/protocol/platform-device.h
@@ -50,6 +50,7 @@
     zx_status_t (*get_board_info)(void* ctx, pdev_board_info_t* out_info);
     zx_status_t (*device_add)(void* ctx, uint32_t index, device_add_args_t* args,
                               zx_device_t** out);
+    zx_status_t (*get_protocol)(void* ctx, uint32_t proto_id, uint32_t index, void* out_protocol);
 } platform_device_protocol_ops_t;
 
 typedef struct {
@@ -110,6 +111,11 @@
     return pdev->ops->device_add(pdev->ctx, index, args, out);
 }
 
+static inline zx_status_t pdev_get_protocol(const platform_device_protocol_t* pdev,
+                                            uint32_t proto_id, uint32_t index, void* out_protocol) {
+    return pdev->ops->get_protocol(pdev->ctx, proto_id, index, out_protocol);
+}
+
 // MMIO mapping helper.
 static inline zx_status_t pdev_map_mmio_buffer(const platform_device_protocol_t* pdev,
                                                uint32_t index, uint32_t cache_policy,
diff --git a/system/ulib/ddk/include/ddk/protodefs.h b/system/ulib/ddk/include/ddk/protodefs.h
index f176621..84f89f8 100644
--- a/system/ulib/ddk/include/ddk/protodefs.h
+++ b/system/ulib/ddk/include/ddk/protodefs.h
@@ -20,7 +20,8 @@
 DDK_PROTOCOL_DEF(ETHERNET,       'pETH', "ethernet", 0)
 DDK_PROTOCOL_DEF(ETHERNET_IMPL,  'pEMA', "ethernet-impl", 0)
 DDK_PROTOCOL_DEF(FRAMEBUFFER,    'pFRB', "framebuffer", 0)
-DDK_PROTOCOL_DEF(GPIO,           'pGPI', "gpio", PF_NOPUB)
+DDK_PROTOCOL_DEF(GPIO,           'pGPO', "gpio", PF_NOPUB)
+DDK_PROTOCOL_DEF(GPIO_IMPL,      'pGPI', "gpio-impl", PF_NOPUB)
 DDK_PROTOCOL_DEF(HIDBUS,         'pHID', "hidbus", 0)
 DDK_PROTOCOL_DEF(I2C,            'pI2C', "i2c", 0)
 DDK_PROTOCOL_DEF(I2C_IMPL ,      'pI2I', "i2c-impl", 0)
diff --git a/system/ulib/ddktl/include/ddktl/gpio_pin.h b/system/ulib/ddktl/include/ddktl/gpio_pin.h
index 68ad885..782a9ab 100644
--- a/system/ulib/ddktl/include/ddktl/gpio_pin.h
+++ b/system/ulib/ddktl/include/ddktl/gpio_pin.h
@@ -24,7 +24,6 @@
     GpioPin(GpioPin&& other) {
         gpio_.ops = other.gpio_.ops;
         gpio_.ctx = other.gpio_.ctx;
-        pdev_index_ = other.pdev_index_;
         other.reset();
     }
 
@@ -34,7 +33,6 @@
     GpioPin& operator=(GpioPin&& other) {
         gpio_.ops = other.gpio_.ops;
         gpio_.ctx = other.gpio_.ctx;
-        pdev_index_ = other.pdev_index_;
         other.reset();
         return *this;
     }
@@ -53,32 +51,32 @@
         properly initialized.
     */
     zx_status_t Read(uint8_t* out) const {
-        return gpio_read(&gpio_, pdev_index_, out);
+        return gpio_read(&gpio_, out);
     }
 
     zx_status_t Write(uint8_t val) const {
-        return gpio_write(&gpio_, pdev_index_, val);
+        return gpio_write(&gpio_, val);
     }
 
     zx_status_t ConfigIn(uint32_t flags) const {
-        return gpio_config_in(&gpio_, pdev_index_, flags);
+        return gpio_config_in(&gpio_, flags);
     }
 
     zx_status_t ConfigOut(uint8_t initial_value) const {
-        return gpio_config_out(&gpio_, pdev_index_, initial_value);
+        return gpio_config_out(&gpio_, initial_value);
     }
 
     zx_status_t SetFunction(uint64_t function) const {
-        return gpio_set_alt_function(&gpio_, pdev_index_, function);
+        return gpio_set_alt_function(&gpio_, function);
     }
 
     zx_status_t GetInterrupt(uint32_t flags, zx::interrupt* out) const {
-        return gpio_get_interrupt(&gpio_, pdev_index_, flags,
+        return gpio_get_interrupt(&gpio_, flags,
                                   out->reset_and_get_address());
     }
 
     zx_status_t SetPolarity(uint32_t polarity) const {
-        return gpio_set_polarity(&gpio_, pdev_index_, polarity);
+        return gpio_set_polarity(&gpio_, polarity);
     }
 
     // Check to determine if this object is intiialized
@@ -91,13 +89,10 @@
         Users must use the Pdev class as a factory for GpioPin
         instances, hence the meaningful constructor(s) are buried.
     */
-    GpioPin(uint32_t index, gpio_protocol_t gpio)
-        : pdev_index_(index),
-          gpio_(gpio) {
+    GpioPin(gpio_protocol_t gpio)
+        : gpio_(gpio) {
     }
 
-    uint32_t pdev_index_;
-
     gpio_protocol_t gpio_ = {nullptr, nullptr};
 };
 
diff --git a/system/ulib/ddktl/include/ddktl/protocol/gpio-impl-internal.h b/system/ulib/ddktl/include/ddktl/protocol/gpio-impl-internal.h
new file mode 100644
index 0000000..f8feb0a
--- /dev/null
+++ b/system/ulib/ddktl/include/ddktl/protocol/gpio-impl-internal.h
@@ -0,0 +1,58 @@
+// 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
+
+#include <fbl/type_support.h>
+
+namespace ddk {
+namespace internal {
+
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_config_in, GpioImplConfigIn,
+        zx_status_t (C::*)(uint32_t, uint32_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_config_out, GpioImplConfigOut,
+        zx_status_t (C::*)(uint32_t, uint8_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_set_alt_function, GpioImplSetAltFunction,
+        zx_status_t (C::*)(uint32_t, uint64_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_read, GpioImplRead,
+        zx_status_t (C::*)(uint32_t, uint8_t*));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_write, GpioImplWrite,
+        zx_status_t (C::*)(uint32_t, uint8_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_get_interrupt, GpioImplGetInterrupt,
+        zx_status_t (C::*)(uint32_t, uint32_t, zx_handle_t*));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_release_interrupt, GpioImplReleaseInterrupt,
+        zx_status_t (C::*)(uint32_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_impl_set_polarity, GpioImplSetPolarity,
+        zx_status_t (C::*)(uint32_t, uint32_t));
+
+template <typename D>
+constexpr void CheckGpioImplProtocolSubclass() {
+    static_assert(internal::has_gpio_impl_config_in<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplConfigIn(uint32_t index, uint32_t flags)");
+    static_assert(internal::has_gpio_impl_config_out<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplConfigOut(uint32_t index, uint8_t initial_value)");
+    static_assert(internal::has_gpio_impl_set_alt_function<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplSetAltFunction(uint32_t index, uint64_t function)");
+    static_assert(internal::has_gpio_impl_read<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplRead(uint32_t index, uint8_t* out_value)");
+    static_assert(internal::has_gpio_impl_write<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplWrite(uint32_t index, uint8_t value)");
+    static_assert(internal::has_gpio_impl_get_interrupt<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplGetInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle)");
+    static_assert(internal::has_gpio_impl_release_interrupt<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplReleaseInterrupt(uint32_t index)");
+    static_assert(internal::has_gpio_impl_set_polarity<D>::value,
+                  "GpioImplProtocol subclasses must implement "
+                  "GpioImplSetPolarity(uint32_t index, uint32_t polarity)");
+ }
+
+}  // namespace internal
+}  // namespace ddk
diff --git a/system/ulib/ddktl/include/ddktl/protocol/gpio-impl.h b/system/ulib/ddktl/include/ddktl/protocol/gpio-impl.h
new file mode 100644
index 0000000..ee4f819
--- /dev/null
+++ b/system/ulib/ddktl/include/ddktl/protocol/gpio-impl.h
@@ -0,0 +1,142 @@
+// 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
+
+#include <ddk/driver.h>
+#include <ddk/protocol/gpio-impl.h>
+#include <ddktl/device-internal.h>
+#include <zircon/assert.h>
+
+#include "gpio-impl-internal.h"
+
+// DDK GPIO implementation protocol support.
+//
+// :: Proxies ::
+//
+// ddk::GpioImplProtocolProxy is a simple wrappers around gpio_impl_protocol_t. It does
+// not own the pointers passed to it.
+//
+// :: Mixins ::
+//
+// ddk::GpioImplProtocol is a mixin class that simplifies writing DDK drivers that
+// implement the GPIO implementation protocol.
+//
+// :: Examples ::
+//
+// // A driver that implements a ZX_PROTOCOL_GPIO_IMPL device.
+// class GpioImplDevice;
+// using GpioImplDeviceType = ddk::Device<GpioImplDevice, /* ddk mixins */>;
+//
+// class GpioImplDevice : public GpioImplDeviceType,
+//                        public ddk::GpioImplProtocol<GpioImplDevice> {
+//   public:
+//     GpioImplDevice(zx_device_t* parent)
+//       : GpioImplDeviceType("my-GpioImpl-device", parent) {}
+//
+//     zx_status_t GpioImplConfigIn(uint32_t index, uint32_t flags);
+//     zx_status_t GpioImplConfigOut(uint32_t index, uint8_t initial_value);
+//     zx_status_t GpioImplSetAltFunction(uint32_t index, uint64_t function);
+//     zx_status_t GpioImplRead(uint32_t index, uint8_t* out_value);
+//     zx_status_t GpioImplWrite(uint32_t index, uint8_t value);
+//     zx_status_t GpioImplGetInterrupt(uint32_t index, uint32_t flags, zx_handle_t *out_handle);
+//     zx_status_t GpioImplReleaseInterrupt(uint32_t index);
+//     zx_status_t GpioImplSetPolarity(uint32_t index, uint32_t polarity);
+//     ...
+// };
+
+namespace ddk {
+
+template <typename D>
+class GpioImplProtocol : public internal::base_protocol {
+public:
+    GpioImplProtocol() {
+        internal::CheckGpioImplProtocolSubclass<D>();
+        ops_.config_in = GpioImplConfigIn;
+        ops_.config_out = GpioImplConfigOut;
+        ops_.set_alt_function = GpioImplSetAltFunction;
+        ops_.read = GpioImplRead;
+        ops_.write = GpioImplWrite;
+        ops_.get_interrupt = GpioImplGetInterrupt;
+        ops_.release_interrupt = GpioImplReleaseInterrupt;
+        ops_.set_polarity = GpioImplSetPolarity;
+
+        // Can only inherit from one base_protocol implemenation
+        ZX_ASSERT(ddk_proto_id_ == 0);
+        ddk_proto_id_ = ZX_PROTOCOL_GPIO_IMPL;
+        ddk_proto_ops_ = &ops_;
+    }
+
+protected:
+    gpio_impl_protocol_ops_t ops_ = {};
+
+private:
+    static zx_status_t GpioImplConfigIn(void* ctx, uint32_t index, uint32_t flags) {
+        return static_cast<D*>(ctx)->GpioImplConfigIn(index, flags);
+    }
+    static zx_status_t GpioImplConfigOut(void* ctx, uint32_t index, uint8_t initial_value) {
+        return static_cast<D*>(ctx)->GpioImplConfigOut(index, initial_value);
+    }
+    static zx_status_t GpioImplSetAltFunction(void* ctx, uint32_t index, uint64_t function) {
+        return static_cast<D*>(ctx)->GpioImplSetAltFunction(index, function);
+    }
+    static zx_status_t GpioImplRead(void* ctx, uint32_t index, uint8_t* out_value) {
+        return static_cast<D*>(ctx)->GpioImplRead(index, out_value);
+    }
+    static zx_status_t GpioImplWrite(void* ctx, uint32_t index, uint8_t value) {
+        return static_cast<D*>(ctx)->GpioImplWrite(index, value);
+    }
+    static zx_status_t GpioImplGetInterrupt(void* ctx, uint32_t index, uint32_t flags,
+                                        zx_handle_t* out_handle) {
+        return static_cast<D*>(ctx)->GpioImplGetInterrupt(index, flags, out_handle);
+    }
+    static zx_status_t GpioImplReleaseInterrupt(void* ctx, uint32_t index) {
+        return static_cast<D*>(ctx)->GpioImplReleaseInterrupt(index);
+    }
+    static zx_status_t GpioImplSetPolarity(void* ctx, uint32_t index, uint32_t polarity) {
+        return static_cast<D*>(ctx)->GpioImplSetPolarity(index, polarity);
+    }
+};
+
+class GpioImplProtocolProxy {
+public:
+    GpioImplProtocolProxy(gpio_impl_protocol_t* proto)
+        : ops_(proto->ops), ctx_(proto->ctx) {}
+
+    void GetProto(gpio_impl_protocol_t* proto) {
+        proto->ctx = ctx_;
+        proto->ops = ops_;
+    }
+
+    zx_status_t ConfigIn(uint32_t index, uint32_t flags) {
+        return ops_->config_in(ctx_, index, flags);
+    }
+    zx_status_t ConfigOut(uint32_t index, uint8_t initial_value) {
+        return ops_->config_out(ctx_, index, initial_value);
+    }
+    zx_status_t SetAltFunction(uint32_t index, uint64_t function) {
+        return ops_->set_alt_function(ctx_, index, function);
+    }
+    zx_status_t Read(uint32_t index, uint8_t* out_value) {
+        return ops_->read(ctx_, index, out_value);
+    }
+    zx_status_t Write(uint32_t index, uint8_t value) {
+        return ops_->write(ctx_, index, value);
+    }
+    zx_status_t GetInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle) {
+        return ops_->get_interrupt(ctx_, index, flags, out_handle);
+    }
+    zx_status_t ReleaseInterrupt(uint32_t index) {
+        return ops_->release_interrupt(ctx_, index);
+    }
+    zx_status_t SetPolarity(uint32_t index, uint32_t polarity) {
+        return ops_->set_polarity(ctx_, index, polarity);
+    }
+
+private:
+    gpio_impl_protocol_ops_t* ops_;
+    void* ctx_;
+};
+
+} // namespace ddk
diff --git a/system/ulib/ddktl/include/ddktl/protocol/gpio-internal.h b/system/ulib/ddktl/include/ddktl/protocol/gpio-internal.h
index a4c0d43..9ead24d 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/gpio-internal.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/gpio-internal.h
@@ -10,48 +10,48 @@
 namespace internal {
 
 DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_config_in, GpioConfigIn,
-        zx_status_t (C::*)(uint32_t, uint32_t));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_config_out, GpioConfigOut,
-        zx_status_t (C::*)(uint32_t, uint8_t));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_set_alt_function, GpioSetAltFunction,
-        zx_status_t (C::*)(uint32_t, uint64_t));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_read, GpioRead,
-        zx_status_t (C::*)(uint32_t, uint8_t*));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_write, GpioWrite,
-        zx_status_t (C::*)(uint32_t, uint8_t));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_get_interrupt, GpioGetInterrupt,
-        zx_status_t (C::*)(uint32_t, uint32_t, zx_handle_t*));
-DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_release_interrupt, GpioReleaseInterrupt,
         zx_status_t (C::*)(uint32_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_config_out, GpioConfigOut,
+        zx_status_t (C::*)(uint8_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_set_alt_function, GpioSetAltFunction,
+        zx_status_t (C::*)(uint64_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_read, GpioRead,
+        zx_status_t (C::*)(uint8_t*));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_write, GpioWrite,
+        zx_status_t (C::*)(uint8_t));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_get_interrupt, GpioGetInterrupt,
+        zx_status_t (C::*)(uint32_t, zx_handle_t*));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_release_interrupt, GpioReleaseInterrupt,
+        zx_status_t (C::*)());
 DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_gpio_set_polarity, GpioSetPolarity,
-        zx_status_t (C::*)(uint32_t, uint32_t));
+        zx_status_t (C::*)(uint32_t));
 
 template <typename D>
 constexpr void CheckGpioProtocolSubclass() {
     static_assert(internal::has_gpio_config_in<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioConfigIn(uint32_t index, uint32_t flags)");
+                  "GpioConfigIn(uint32_t flags)");
     static_assert(internal::has_gpio_config_out<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioConfigOut(uint32_t index, uint8_t initial_value)");
+                  "GpioConfigOut(uint8_t initial_value)");
     static_assert(internal::has_gpio_set_alt_function<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioSetAltFunction(uint32_t index, uint64_t function)");
+                  "GpioSetAltFunction(uint64_t function)");
     static_assert(internal::has_gpio_read<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioRead(uint32_t index, uint8_t* out_value)");
+                  "GpioRead(uint8_t* out_value)");
     static_assert(internal::has_gpio_write<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioWrite(uint32_t index, uint8_t value)");
+                  "GpioWrite(uint8_t value)");
     static_assert(internal::has_gpio_get_interrupt<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioGetInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle)");
+                  "GpioGetInterrupt(uint32_t flags, zx_handle_t* out_handle)");
     static_assert(internal::has_gpio_release_interrupt<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioReleaseInterrupt(uint32_t index)");
+                  "GpioReleaseInterrupt()");
     static_assert(internal::has_gpio_set_polarity<D>::value,
                   "GpioProtocol subclasses must implement "
-                  "GpioSetPolarity(uint32_t index, uint32_t polarity)");
+                  "GpioSetPolarity(uint32_t polarity)");
  }
 
 }  // namespace internal
diff --git a/system/ulib/ddktl/include/ddktl/protocol/gpio.h b/system/ulib/ddktl/include/ddktl/protocol/gpio.h
index 6c5ae88..7fe93be 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/gpio.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/gpio.h
@@ -35,14 +35,14 @@
 //     GpioDevice(zx_device_t* parent)
 //       : GpioDeviceType("my-gpio-device", parent) {}
 //
-//     zx_status_t GpioConfigIn(uint32_t index, uint32_t flags);
-//     zx_status_t GpioConfigOut(uint32_t index, uint8_t initial_value);
-//     zx_status_t GpioSetAltFunction(uint32_t index, uint64_t function);
-//     zx_status_t GpioRead(uint32_t index, uint8_t* out_value);
-//     zx_status_t GpioWrite(uint32_t index, uint8_t value);
-//     zx_status_t GpioGetInterrupt(uint32_t index, uint32_t flags, zx_handle_t *out_handle);
-//     zx_status_t GpioReleaseInterrupt(uint32_t index);
-//     zx_status_t GpioSetPolarity(uint32_t index, uint32_t polarity);
+//     zx_status_t GpioConfigIn(uint32_t flags);
+//     zx_status_t GpioConfigOut(uint8_t initial_value);
+//     zx_status_t GpioSetAltFunction(uint64_t function);
+//     zx_status_t GpioRead(uint8_t* out_value);
+//     zx_status_t GpioWrite(uint8_t value);
+//     zx_status_t GpioGetInterrupt(uint32_t flags, zx_handle_t *out_handle);
+//     zx_status_t GpioReleaseInterrupt();
+//     zx_status_t GpioSetPolarity(uint32_t polarity);
 //     ...
 // };
 
@@ -72,30 +72,30 @@
     gpio_protocol_ops_t ops_ = {};
 
 private:
-    static zx_status_t GpioConfigIn(void* ctx, uint32_t index, uint32_t flags) {
-        return static_cast<D*>(ctx)->GpioConfigIn(index, flags);
+    static zx_status_t GpioConfigIn(void* ctx, uint32_t flags) {
+        return static_cast<D*>(ctx)->GpioConfigIn(flags);
     }
-    static zx_status_t GpioConfigOut(void* ctx, uint32_t index, uint8_t initial_value) {
-        return static_cast<D*>(ctx)->GpioConfigOut(index, initial_value);
+    static zx_status_t GpioConfigOut(void* ctx, uint8_t initial_value) {
+        return static_cast<D*>(ctx)->GpioConfigOut(initial_value);
     }
-    static zx_status_t GpioSetAltFunction(void* ctx, uint32_t index, uint64_t function) {
-        return static_cast<D*>(ctx)->GpioSetAltFunction(index, function);
+    static zx_status_t GpioSetAltFunction(void* ctx, uint64_t function) {
+        return static_cast<D*>(ctx)->GpioSetAltFunction(function);
     }
-    static zx_status_t GpioRead(void* ctx, uint32_t index, uint8_t* out_value) {
-        return static_cast<D*>(ctx)->GpioRead(index, out_value);
+    static zx_status_t GpioRead(void* ctx, uint8_t* out_value) {
+        return static_cast<D*>(ctx)->GpioRead(out_value);
     }
-    static zx_status_t GpioWrite(void* ctx, uint32_t index, uint8_t value) {
-        return static_cast<D*>(ctx)->GpioWrite(index, value);
+    static zx_status_t GpioWrite(void* ctx, uint8_t value) {
+        return static_cast<D*>(ctx)->GpioWrite(value);
     }
-    static zx_status_t GpioGetInterrupt(void* ctx, uint32_t index, uint32_t flags,
+    static zx_status_t GpioGetInterrupt(void* ctx, uint32_t flags,
                                         zx_handle_t* out_handle) {
-        return static_cast<D*>(ctx)->GpioGetInterrupt(index, flags, out_handle);
+        return static_cast<D*>(ctx)->GpioGetInterrupt(flags, out_handle);
     }
-    static zx_status_t GpioReleaseInterrupt(void* ctx, uint32_t index) {
-        return static_cast<D*>(ctx)->GpioReleaseInterrupt(index);
+    static zx_status_t GpioReleaseInterrupt(void* ctx) {
+        return static_cast<D*>(ctx)->GpioReleaseInterrupt();
     }
-    static zx_status_t GpioSetPolarity(void* ctx, uint32_t index, uint32_t polarity) {
-        return static_cast<D*>(ctx)->GpioSetPolarity(index, polarity);
+    static zx_status_t GpioSetPolarity(void* ctx, uint32_t polarity) {
+        return static_cast<D*>(ctx)->GpioSetPolarity(polarity);
     }
 };
 
@@ -109,29 +109,29 @@
         proto->ops = ops_;
     }
 
-    zx_status_t ConfigIn(uint32_t index, uint32_t flags) {
-        return ops_->config_in(ctx_, index, flags);
+    zx_status_t ConfigIn(uint32_t flags) {
+        return ops_->config_in(ctx_, flags);
     }
-    zx_status_t ConfigOut(uint32_t index, uint8_t initial_value) {
-        return ops_->config_out(ctx_, index, initial_value);
+    zx_status_t ConfigOut(uint8_t initial_value) {
+        return ops_->config_out(ctx_, initial_value);
     }
-    zx_status_t SetAltFunction(uint32_t index, uint64_t function) {
-        return ops_->set_alt_function(ctx_, index, function);
+    zx_status_t SetAltFunction(uint64_t function) {
+        return ops_->set_alt_function(ctx_, function);
     }
-    zx_status_t Read(uint32_t index, uint8_t* out_value) {
-        return ops_->read(ctx_, index, out_value);
+    zx_status_t Read(uint8_t* out_value) {
+        return ops_->read(ctx_, out_value);
     }
-    zx_status_t Write(uint32_t index, uint8_t value) {
-        return ops_->write(ctx_, index, value);
+    zx_status_t Write(uint8_t value) {
+        return ops_->write(ctx_, value);
     }
-    zx_status_t GetInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle) {
-        return ops_->get_interrupt(ctx_, index, flags, out_handle);
+    zx_status_t GetInterrupt(uint32_t flags, zx_handle_t* out_handle) {
+        return ops_->get_interrupt(ctx_, flags, out_handle);
     }
-    zx_status_t ReleaseInterrupt(uint32_t index) {
-        return ops_->release_interrupt(ctx_, index);
+    zx_status_t ReleaseInterrupt() {
+        return ops_->release_interrupt(ctx_);
     }
-    zx_status_t SetPolarity(uint32_t index, uint32_t polarity) {
-        return ops_->set_polarity(ctx_, index, polarity);
+    zx_status_t SetPolarity(uint32_t polarity) {
+        return ops_->set_polarity(ctx_, polarity);
     }
 
 private:
diff --git a/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h b/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
index b16f788..111c310 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/i2c-impl.h
@@ -11,7 +11,7 @@
 
 #include "i2c-impl-internal.h"
 
-// DDK I2C protocol support.
+// DDK I2C implementation protocol support.
 //
 // :: Proxies ::
 //
@@ -21,7 +21,7 @@
 // :: Mixins ::
 //
 // ddk::I2cImplProtocol is a mixin class that simplifies writing DDK drivers that
-// implement the I2C protocol.
+// implement the I2C implementation protocol.
 //
 // :: Examples ::
 //
diff --git a/system/ulib/ddktl/include/ddktl/protocol/platform-device-internal.h b/system/ulib/ddktl/include/ddktl/protocol/platform-device-internal.h
index e80577e..45d81dd 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/platform-device-internal.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/platform-device-internal.h
@@ -20,7 +20,9 @@
 DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_pdev_get_board_info, GetBoardInfo,
         zx_status_t (C::*)(pdev_board_info_t*));
 DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_pdev_device_add, DeviceAdd,
-        zx_status_t (C::*)(uint32_t, device_add_args_t*,  zx_device_t**));
+        zx_status_t (C::*)(uint32_t, device_add_args_t*, zx_device_t**));
+DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_pdev_get_protocol, GetProtocol,
+        zx_status_t (C::*)(uint32_t, uint32_t, void*));
 
 template <typename D>
 constexpr void CheckPlatformDevProtocolSubclass() {
@@ -43,7 +45,10 @@
     static_assert(internal::has_pdev_device_add<D>::value,
                   "PlatformDevProtocol subclasses must implement "
                   "DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out)");
- }
+    static_assert(internal::has_pdev_get_protocol<D>::value,
+                  "PlatformDevProtocol subclasses must implement "
+                  "GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol)");
+}
 
 }  // namespace internal
 }  // namespace ddk
diff --git a/system/ulib/ddktl/include/ddktl/protocol/platform-device.h b/system/ulib/ddktl/include/ddktl/protocol/platform-device.h
index 4a893fd..2f2492e 100644
--- a/system/ulib/ddktl/include/ddktl/protocol/platform-device.h
+++ b/system/ulib/ddktl/include/ddktl/protocol/platform-device.h
@@ -42,6 +42,7 @@
 //        zx_status_t GetDeviceInfo(pdev_device_info_t* out_info);
 //        zx_status_t GetBoardInfo(pdev_board_info_t* out_info);
 //        zx_status_t DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out);
+//        zx_status_t GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol);
 //     ...
 // };
 
@@ -58,6 +59,7 @@
         pdev_proto_ops_.get_device_info = GetDeviceInfo;
         pdev_proto_ops_.get_board_info = GetBoardInfo;
         pdev_proto_ops_.device_add = DeviceAdd;
+        pdev_proto_ops_.get_protocol = GetProtocol;
 
         // Can only inherit from one base_protocol implementation.
         ZX_ASSERT(ddk_proto_id_ == 0);
@@ -96,6 +98,11 @@
                                  zx_device_t** out) {
         return static_cast<D*>(ctx)->DeviceAdd(index, args, out);
     }
+
+    static zx_status_t GetProtocol(void* ctx, uint32_t proto_id, uint32_t index,
+                                   void* out_protocol) {
+        return static_cast<D*>(ctx)->GetProtocol(proto_id, index, out_protocol);
+    }
 };
 
 class PlatformDevProtocolProxy {
@@ -129,6 +136,10 @@
         return ops_->device_add(ctx_, index, args, out);
     }
 
+    zx_status_t GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol) {
+        return ops_->get_protocol(ctx_, proto_id, index, out_protocol);
+    }
+
 private:
     platform_device_protocol_ops_t* ops_;
     void* ctx_;
diff --git a/system/ulib/ddktl/pdev.cpp b/system/ulib/ddktl/pdev.cpp
index f4051fc..a09c2c0 100644
--- a/system/ulib/ddktl/pdev.cpp
+++ b/system/ulib/ddktl/pdev.cpp
@@ -56,11 +56,11 @@
 
     gpio_protocol_t gpio;
     //Note: Pdev is a friend class of GpioPin
-    zx_status_t res = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio);
+    zx_status_t res = pdev_get_protocol(&pdev_, ZX_PROTOCOL_GPIO, index, &gpio);
     if (res != ZX_OK) {
         return GpioPin();
     }
-    return GpioPin(index, gpio);
+    return GpioPin(gpio);
 }
 
 fbl::RefPtr<Pdev> Pdev::Create(zx_device_t* parent) {