1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdint.h>
6#include <threads.h>
7
8#include <bits/limits.h>
9#include <ddk/binding.h>
10#include <ddk/debug.h>
11#include <ddk/device.h>
12#include <ddk/mmio-buffer.h>
13#include <ddk/protocol/gpio-impl.h>
14#include <ddk/protocol/platform-bus.h>
15#include <ddk/protocol/platform-defs.h>
16#include <ddk/protocol/platform-device.h>
17#include <zircon/syscalls/port.h>
18
19#include <hw/reg.h>
20#include <soc/imx8m/imx8m-gpio.h>
21#include <soc/imx8m/imx8m-hw.h>
22#include <soc/imx8m/imx8m-iomux.h>
23#include <zircon/assert.h>
24#include <zircon/types.h>
25
26typedef struct {
27    platform_device_protocol_t pdev;
28    platform_bus_protocol_t pbus;
29    gpio_impl_protocol_t gpio;
30    zx_device_t* zxdev;
31    mmio_buffer_t mmios[IMX_GPIO_BLOCKS];
32    mmio_buffer_t mmio_iomux;
33    mtx_t lock[IMX_GPIO_BLOCKS];
34    zx_handle_t inth[IMX_GPIO_INTERRUPTS];
35    zx_handle_t vinth[IMX_GPIO_MAX];
36    zx_handle_t porth;
37    thrd_t irq_handler;
38    mtx_t gpio_lock;
39} imx8_gpio_t;
40
41#define READ32_GPIO_REG(block_index, offset) \
42        readl((uint8_t*)gpio->mmios[block_index].vaddr + offset)
43#define WRITE32_GPIO_REG(block_index, offset, value) \
44        writel(value, (uint8_t*)gpio->mmios[block_index].vaddr + offset)
45
46static zx_status_t imx8_gpio_config_in(void* ctx, uint32_t pin, uint32_t flags) {
47    uint32_t gpio_block;
48    uint32_t gpio_pin;
49    uint32_t regVal;
50    imx8_gpio_t* gpio = ctx;
51
52    gpio_block = IMX_NUM_TO_BLOCK(pin);
53    gpio_pin = IMX_NUM_TO_BIT(pin);
54
55    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= 32) {
56        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
57               __FUNCTION__, pin, gpio_block, gpio_pin);
58        return ZX_ERR_INVALID_ARGS;
59    }
60
61    mtx_lock(&gpio->lock[gpio_block]);
62    regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_GDIR);
63    regVal &= ~(1 << gpio_pin);
64    regVal |= (GPIO_INPUT << gpio_pin);
65    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_GDIR, regVal);
66    mtx_unlock(&gpio->lock[gpio_block]);
67    return ZX_OK;
68}
69
70static zx_status_t imx8_gpio_config_out(void* ctx, uint32_t pin, uint8_t initial_value) {
71    uint32_t gpio_block;
72    uint32_t gpio_pin;
73    uint32_t regVal;
74    imx8_gpio_t* gpio = ctx;
75
76    gpio_block = IMX_NUM_TO_BLOCK(pin);
77    gpio_pin = IMX_NUM_TO_BIT(pin);
78
79    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= 32) {
80        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
81               __FUNCTION__, pin, gpio_block, gpio_pin);
82        return ZX_ERR_INVALID_ARGS;
83    }
84
85    mtx_lock(&gpio->lock[gpio_block]);
86
87    // Set value before configuring for output
88    regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_DR);
89    regVal &= ~(1 << gpio_pin);
90    regVal |= (initial_value << gpio_pin);
91    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_DR, regVal);
92
93    regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_GDIR);
94    regVal &= ~(1 << gpio_pin);
95    regVal |= (GPIO_OUTPUT << gpio_pin);
96    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_GDIR, regVal);
97    mtx_unlock(&gpio->lock[gpio_block]);
98    return ZX_OK;
99}
100
101static zx_status_t imx8_gpio_read(void* ctx, uint32_t pin, uint8_t* out_value) {
102    uint32_t gpio_block;
103    uint32_t gpio_pin;
104    uint32_t regVal;
105    imx8_gpio_t* gpio = ctx;
106
107    gpio_block = IMX_NUM_TO_BLOCK(pin);
108    gpio_pin = IMX_NUM_TO_BIT(pin);
109
110    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= 32) {
111        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
112               __FUNCTION__, pin, gpio_block, gpio_pin);
113        return ZX_ERR_INVALID_ARGS;
114    }
115
116    mtx_lock(&gpio->lock[gpio_block]);
117    regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_DR);
118    regVal >>= (gpio_pin);
119    regVal &= 1;
120    *out_value = regVal;
121    mtx_unlock(&gpio->lock[gpio_block]);
122
123    return ZX_OK;
124}
125
126static zx_status_t imx8_gpio_write(void* ctx, uint32_t pin, uint8_t value) {
127    uint32_t gpio_block;
128    uint32_t gpio_pin;
129    uint32_t regVal;
130    imx8_gpio_t* gpio = ctx;
131
132    gpio_block = IMX_NUM_TO_BLOCK(pin);
133    gpio_pin = IMX_NUM_TO_BIT(pin);
134    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= 32) {
135        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
136               __FUNCTION__, pin, gpio_block, gpio_pin);
137        return ZX_ERR_INVALID_ARGS;
138    }
139
140    mtx_lock(&gpio->lock[gpio_block]);
141    regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_DR);
142    regVal &= ~(1 << gpio_pin);
143    regVal |= (value << gpio_pin);
144    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_DR, regVal);
145    mtx_unlock(&gpio->lock[gpio_block]);
146
147    return ZX_OK;
148}
149
150// Configure a pin for an alternate function specified by fn
151static zx_status_t imx8_gpio_set_alt_function(void* ctx, const uint32_t pin, const uint64_t fn) {
152    imx8_gpio_t* gpio = ctx;
153    iomux_cfg_struct s_cfg = (iomux_cfg_struct)fn;
154
155    volatile uint8_t* iomux = (volatile uint8_t*)gpio->mmio_iomux.vaddr;
156
157    zxlogf(SPEW, "0x%lx\n", s_cfg);
158    zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
159           IOMUX_CFG_MUX_MODE_VAL(GET_MUX_MODE_VAL(s_cfg)) |
160               IOMUX_CFG_SION_VAL(GET_SION_VAL(s_cfg)),
161           iomux + GET_MUX_CTL_OFF_VAL(s_cfg));
162    zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
163           IOMUX_CFG_DSE_VAL(GET_DSE_VAL(s_cfg)) |
164               IOMUX_CFG_SRE_VAL(GET_SRE_VAL(s_cfg)) |
165               IOMUX_CFG_ODE_VAL(GET_ODE_VAL(s_cfg)) |
166               IOMUX_CFG_PUE_VAL(GET_PUE_VAL(s_cfg)) |
167               IOMUX_CFG_HYS_VAL(GET_HYS_VAL(s_cfg)) |
168               IOMUX_CFG_LVTTL_VAL(GET_LVTTL_VAL(s_cfg)) |
169               IOMUX_CFG_VSEL_VAL(GET_VSEL_VAL(s_cfg)),
170           iomux + GET_PAD_CTL_OFF_VAL(s_cfg));
171    zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
172           IOMUX_CFG_DAISY_VAL(GET_DAISY_VAL(s_cfg)),
173           iomux + GET_SEL_INP_OFF_VAL(s_cfg));
174
175    if (GET_MUX_CTL_OFF_VAL(s_cfg)) {
176        writel(
177            IOMUX_CFG_MUX_MODE_VAL(GET_MUX_MODE_VAL(s_cfg)) |
178                IOMUX_CFG_SION_VAL(GET_SION_VAL(s_cfg)),
179            iomux + GET_MUX_CTL_OFF_VAL(s_cfg));
180    }
181    if (GET_PAD_CTL_OFF_VAL(s_cfg)) {
182        writel(
183            IOMUX_CFG_DSE_VAL(GET_DSE_VAL(s_cfg)) |
184                IOMUX_CFG_SRE_VAL(GET_SRE_VAL(s_cfg)) |
185                IOMUX_CFG_ODE_VAL(GET_ODE_VAL(s_cfg)) |
186                IOMUX_CFG_PUE_VAL(GET_PUE_VAL(s_cfg)) |
187                IOMUX_CFG_HYS_VAL(GET_HYS_VAL(s_cfg)) |
188                IOMUX_CFG_LVTTL_VAL(GET_LVTTL_VAL(s_cfg)) |
189                IOMUX_CFG_VSEL_VAL(GET_VSEL_VAL(s_cfg)),
190            iomux + GET_PAD_CTL_OFF_VAL(s_cfg));
191    }
192    if (GET_SEL_INP_OFF_VAL(s_cfg)) {
193        writel(IOMUX_CFG_DAISY_VAL(GET_DAISY_VAL(s_cfg)),
194               iomux + GET_SEL_INP_OFF_VAL(s_cfg));
195    }
196
197    return ZX_OK;
198}
199
200static void imx8_gpio_mask_irq(imx8_gpio_t* gpio, uint32_t gpio_block, uint32_t gpio_pin) {
201    uint32_t regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_IMR);
202    regVal &= ~(1 << gpio_pin);
203    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_IMR, regVal);
204}
205
206static void imx8_gpio_unmask_irq(imx8_gpio_t* gpio, uint32_t gpio_block, uint32_t gpio_pin) {
207    uint32_t regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_IMR);
208    regVal |= (1 << gpio_pin);
209    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_IMR, regVal);
210}
211
212static int imx8_gpio_irq_handler(void* arg) {
213    imx8_gpio_t* gpio = arg;
214    zx_port_packet_t packet;
215    zx_status_t status = ZX_OK;
216    uint32_t gpio_block;
217    uint32_t isr;
218    uint32_t imr;
219    uint32_t pin;
220
221    while (1) {
222        status = zx_port_wait(gpio->porth, ZX_TIME_INFINITE, &packet);
223        if (status != ZX_OK) {
224            zxlogf(ERROR, "%s: zx_port_wait failed %d \n", __FUNCTION__, status);
225            goto fail;
226        }
227        zxlogf(INFO, "GPIO Interrupt %x triggered\n", (unsigned int)packet.key);
228        status = zx_interrupt_ack(gpio->inth[packet.key]);
229        if (status != ZX_OK) {
230            zxlogf(ERROR, "%s: zx_interrupt_ack failed %d \n", __FUNCTION__, status);
231            goto fail;
232        }
233
234        gpio_block = IMX_INT_NUM_TO_BLOCK(packet.key);
235        isr = READ32_GPIO_REG(gpio_block, IMX_GPIO_ISR);
236
237        imr = READ32_GPIO_REG(gpio_block, IMX_GPIO_IMR);
238
239        // Get the status of the enabled interrupts
240        // Get the last valid interrupt pin
241        uint32_t valid_irqs = (isr & imr);
242        if (valid_irqs) {
243            pin = __builtin_ctz(valid_irqs);
244            WRITE32_GPIO_REG(gpio_block, IMX_GPIO_ISR, 1 << pin);
245            pin = gpio_block * IMX_GPIO_PER_BLOCK + pin;
246
247            if (gpio->vinth[pin] != ZX_HANDLE_INVALID) {
248                // Trigger the corresponding virtual interrupt
249                status = zx_interrupt_trigger(gpio->vinth[pin], 0, zx_clock_get_monotonic());
250                if (status != ZX_OK) {
251                    zxlogf(ERROR, "%s: zx_interrupt_trigger failed %d \n", __FUNCTION__, status);
252                    goto fail;
253                }
254            }
255        }
256    }
257
258fail:
259    for (int i = 0; i < IMX_GPIO_INTERRUPTS; i++) {
260        zx_interrupt_destroy(gpio->inth[i]);
261        zx_handle_close(gpio->inth[i]);
262    }
263    return status;
264}
265
266static zx_status_t imx8_gpio_get_interrupt(void* ctx, uint32_t pin,
267                                           uint32_t flags,
268                                           zx_handle_t* out_handle) {
269    uint32_t gpio_block;
270    uint32_t gpio_pin;
271    imx8_gpio_t* gpio = ctx;
272    uint32_t regVal;
273    uint32_t interrupt_type;
274    zx_status_t status = ZX_OK;
275    uint32_t icr_offset;
276
277    gpio_block = IMX_NUM_TO_BLOCK(pin);
278    gpio_pin = IMX_NUM_TO_BIT(pin);
279    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= IMX_GPIO_PER_BLOCK) {
280        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
281               __FUNCTION__, pin, gpio_block, gpio_pin);
282        return ZX_ERR_INVALID_ARGS;
283    }
284
285    // Create Virtual Interrupt
286    status = zx_interrupt_create(0, 0, ZX_INTERRUPT_VIRTUAL, &gpio->vinth[pin]);
287    if (status != ZX_OK) {
288        zxlogf(ERROR, "%s: zx_irq_create failed %d \n", __FUNCTION__, status);
289        return status;
290    }
291
292    // Store the Virtual Interrupt
293    status = zx_handle_duplicate(gpio->vinth[pin], ZX_RIGHT_SAME_RIGHTS, out_handle);
294    if (status != ZX_OK) {
295        zxlogf(ERROR, "%s: zx_handle_duplicate failed %d \n", __FUNCTION__, status);
296        return status;
297    }
298
299    mtx_lock(&gpio->lock[gpio_block]);
300    // Select EGDE or LEVEL and polarity
301    switch (flags & ZX_INTERRUPT_MODE_MASK) {
302    case ZX_INTERRUPT_MODE_EDGE_LOW:
303        interrupt_type = IMX_GPIO_FALLING_EDGE_INTERRUPT;
304        break;
305    case ZX_INTERRUPT_MODE_EDGE_HIGH:
306        interrupt_type = IMX_GPIO_RISING_EDGE_INTERRUPT;
307        break;
308    case ZX_INTERRUPT_MODE_LEVEL_LOW:
309        interrupt_type = IMX_GPIO_LOW_LEVEL_INTERRUPT;
310        break;
311    case ZX_INTERRUPT_MODE_LEVEL_HIGH:
312        interrupt_type = IMX_GPIO_HIGH_LEVEL_INTERRUPT;
313        break;
314    case ZX_INTERRUPT_MODE_EDGE_BOTH:
315        interrupt_type = IMX_GPIO_BOTH_EDGE_INTERRUPT;
316        break;
317    default:
318        status = ZX_ERR_INVALID_ARGS;
319        goto fail;
320    }
321
322    if (interrupt_type == IMX_GPIO_BOTH_EDGE_INTERRUPT) {
323        regVal = READ32_GPIO_REG(gpio_block, IMX_GPIO_EDGE_SEL);
324        regVal |= (1 << gpio_pin);
325        WRITE32_GPIO_REG(gpio_block, IMX_GPIO_EDGE_SEL, regVal);
326    } else {
327        // Select which ICR register to program
328        if (gpio_pin >= IMX_GPIO_MAX_ICR_PIN) {
329            icr_offset = IMX_GPIO_ICR2;
330        } else {
331            icr_offset = IMX_GPIO_ICR1;
332        }
333        regVal = READ32_GPIO_REG(gpio_block, icr_offset);
334        regVal &= ~(IMX_GPIO_ICR_MASK << IMX_GPIO_ICR_SHIFT(gpio_pin));
335        regVal |= (interrupt_type << IMX_GPIO_ICR_SHIFT(gpio_pin));
336        WRITE32_GPIO_REG(gpio_block, icr_offset, regVal);
337    }
338
339    // Mask the Interrupt
340    imx8_gpio_mask_irq(gpio, gpio_block, gpio_pin);
341
342    // Clear the Interrupt Status
343    WRITE32_GPIO_REG(gpio_block, IMX_GPIO_ISR, 1 << gpio_pin);
344
345    // Unmask the Interrupt
346    imx8_gpio_unmask_irq(gpio, gpio_block, gpio_pin);
347
348fail:
349    mtx_unlock(&gpio->lock[gpio_block]);
350    return status;
351}
352
353static zx_status_t imx8_gpio_release_interrupt(void* ctx, uint32_t pin) {
354    imx8_gpio_t* gpio = ctx;
355    zx_status_t status = ZX_OK;
356    uint32_t gpio_pin = IMX_NUM_TO_BIT(pin);
357    uint32_t gpio_block = IMX_NUM_TO_BLOCK(pin);
358    if (gpio_block >= IMX_GPIO_BLOCKS || gpio_pin >= IMX_GPIO_PER_BLOCK) {
359        zxlogf(ERROR, "%s: Invalid GPIO pin (pin = %d Block = %d, Offset = %d)\n",
360               __FUNCTION__, pin, gpio_block, gpio_pin);
361        return ZX_ERR_INVALID_ARGS;
362    }
363    mtx_lock(&gpio->gpio_lock);
364    // Mask the interrupt
365    imx8_gpio_mask_irq(gpio, gpio_block, gpio_pin);
366
367    zx_handle_close(gpio->vinth[pin]);
368    gpio->vinth[pin] = ZX_HANDLE_INVALID;
369    if (status != ZX_OK) {
370        zxlogf(ERROR, "%s: zx_handle_close failed %d \n", __FUNCTION__, status);
371        goto fail;
372    }
373
374fail:
375    mtx_unlock(&gpio->gpio_lock);
376    return status;
377}
378
379static zx_status_t imx8_gpio_set_polarity(void* ctx, uint32_t pin,
380                                          uint32_t polarity) {
381    return ZX_ERR_NOT_SUPPORTED;
382}
383
384static gpio_impl_protocol_ops_t gpio_ops = {
385    .config_in = imx8_gpio_config_in,
386    .config_out = imx8_gpio_config_out,
387    .set_alt_function = imx8_gpio_set_alt_function,
388    .read = imx8_gpio_read,
389    .write = imx8_gpio_write,
390    .get_interrupt = imx8_gpio_get_interrupt,
391    .release_interrupt = imx8_gpio_release_interrupt,
392    .set_polarity = imx8_gpio_set_polarity,
393};
394
395static void imx8_gpio_release(void* ctx) {
396    unsigned i;
397    imx8_gpio_t* gpio = ctx;
398    mtx_lock(&gpio->gpio_lock);
399    for (i = 0; i < IMX_GPIO_BLOCKS; i++) {
400        mmio_buffer_release(&gpio->mmios[i]);
401    }
402    mmio_buffer_release(&gpio->mmio_iomux);
403
404    for (int i = 0; i < IMX_GPIO_INTERRUPTS; i++) {
405        zx_interrupt_destroy(gpio->inth[i]);
406        zx_handle_close(gpio->inth[i]);
407    }
408    free(gpio);
409    mtx_unlock(&gpio->gpio_lock);
410}
411
412static zx_protocol_device_t gpio_device_proto = {
413    .version = DEVICE_OPS_VERSION,
414    .release = imx8_gpio_release,
415};
416
417static zx_status_t imx8_gpio_bind(void* ctx, zx_device_t* parent) {
418    zx_status_t status;
419    unsigned i;
420
421    imx8_gpio_t* gpio = calloc(1, sizeof(imx8_gpio_t));
422    if (!gpio) {
423        return ZX_ERR_NO_MEMORY;
424    }
425
426    status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &gpio->pdev);
427    if (status != ZX_OK) {
428        zxlogf(ERROR, "%s: ZX_PROTOCOL_PLATFORM_DEV not available %d \n", __FUNCTION__, status);
429        goto fail;
430    }
431
432    status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &gpio->pbus);
433    if (status != ZX_OK) {
434        zxlogf(ERROR, "%s: ZX_PROTOCOL_PLATFORM_BUS not available %d\n", __FUNCTION__, status);
435        goto fail;
436    }
437
438    for (i = 0; i < IMX_GPIO_BLOCKS; i++) {
439        status = pdev_map_mmio_buffer2(&gpio->pdev, i, ZX_CACHE_POLICY_UNCACHED_DEVICE,
440                                       &gpio->mmios[i]);
441        if (status != ZX_OK) {
442            zxlogf(ERROR, "%s: pdev_map_mmio_buffer gpio failed %d\n", __FUNCTION__, status);
443            goto fail;
444        }
445
446        mtx_init(&gpio->lock[i], mtx_plain);
447    }
448
449    status = pdev_map_mmio_buffer2(&gpio->pdev, IMX_GPIO_BLOCKS, ZX_CACHE_POLICY_UNCACHED_DEVICE,
450                                   &gpio->mmio_iomux);
451    if (status != ZX_OK) {
452        zxlogf(ERROR, "%s: pdev_map_mmio_buffer iomux failed %d\n", __FUNCTION__, status);
453        goto fail;
454    }
455
456    pdev_device_info_t info;
457    status = pdev_get_device_info(&gpio->pdev, &info);
458    if (status != ZX_OK) {
459        zxlogf(ERROR, "%s: pdev_get_device_info failed %d\n", __FUNCTION__, status);
460        goto fail;
461    }
462
463    status = zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &gpio->porth);
464    if (status != ZX_OK) {
465        zxlogf(ERROR, "%s: zx_port_create failed %d\n", __FUNCTION__, status);
466        goto fail;
467    }
468
469    for (i = 0; i < info.irq_count; i++) {
470        // Create Interrupt Object
471        status = pdev_map_interrupt(&gpio->pdev, i,
472                                    &gpio->inth[i]);
473        if (status != ZX_OK) {
474            zxlogf(ERROR, "%s: pdev_map_interrupt failed %d\n", __FUNCTION__, status);
475            goto fail;
476        }
477        // The KEY is the Interrupt Number for our usecase
478        status = zx_interrupt_bind(gpio->inth[i], gpio->porth, i, 0 /*optons*/);
479        if (status != ZX_OK) {
480            zxlogf(ERROR, "%s: zx_interrupt_bind failed %d\n", __FUNCTION__, status);
481            goto fail;
482        }
483    }
484
485    thrd_create_with_name(&gpio->irq_handler, imx8_gpio_irq_handler, gpio, "imx8_gpio_irq_handler");
486
487    device_add_args_t args = {
488        .version = DEVICE_ADD_ARGS_VERSION,
489        .name = "imx8-gpio",
490        .ctx = gpio,
491        .ops = &gpio_device_proto,
492        .flags = DEVICE_ADD_NON_BINDABLE,
493    };
494
495    status = device_add(parent, &args, &gpio->zxdev);
496    if (status != ZX_OK) {
497        zxlogf(ERROR, "%s: device_add failed! %d\n", __FUNCTION__, status);
498        goto fail;
499    }
500
501    gpio->gpio.ops = &gpio_ops;
502    gpio->gpio.ctx = gpio;
503    pbus_register_protocol(&gpio->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio->gpio, NULL, NULL);
504
505    return ZX_OK;
506
507fail:
508    imx8_gpio_release(gpio);
509    return status;
510}
511
512static zx_driver_ops_t imx8_gpio_driver_ops = {
513    .version = DRIVER_OPS_VERSION,
514    .bind = imx8_gpio_bind,
515};
516
517ZIRCON_DRIVER_BEGIN(imx8_gpio, imx8_gpio_driver_ops, "zircon", "0.1", 6)
518    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
519    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_NXP),
520    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_IMX_GPIO),
521    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_IMX8MEVK),
522ZIRCON_DRIVER_END(imx8_gpio)
523