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