| // 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. |
| |
| #include <limits.h> |
| #include <stdio.h> |
| |
| #include <gpio/pl061/pl061.h> |
| #include <hw/reg.h> |
| |
| // GPIO register offsets |
| #define GPIODATA(mask) ((mask) << 2) // Data registers, mask provided as index |
| #define GPIODIR 0x400 // Data direction register (0 = IN, 1 = OUT) |
| #define GPIOIS 0x404 // Interrupt sense register (0 = edge, 1 = level) |
| #define GPIOIBE 0x408 // Interrupt both edges register (1 = both) |
| #define GPIOIEV 0x40C // Interrupt event register (0 = falling, 1 = rising) |
| #define GPIOIE 0x410 // Interrupt mask register (1 = interrupt masked) |
| #define GPIORIS 0x414 // Raw interrupt status register |
| #define GPIOMIS 0x418 // Masked interrupt status register |
| #define GPIOIC 0x41C // Interrupt clear register |
| #define GPIOAFSEL 0x420 // Mode control select register |
| |
| #define GPIOS_PER_PAGE 8 |
| |
| static zx_status_t pl061_gpio_config_in(void* ctx, uint32_t index, uint32_t flags) { |
| pl061_gpios_t* gpios = ctx; |
| index -= gpios->gpio_start; |
| MMIO_PTR volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE); |
| uint8_t bit = 1 << (index % GPIOS_PER_PAGE); |
| |
| mtx_lock(&gpios->lock); |
| uint8_t dir = MmioRead8(regs + GPIODIR); |
| dir &= ~bit; |
| MmioWrite8(dir, regs + GPIODIR); |
| |
| /* TODO(voydanoff) this should move to a gpio_get_interrupt callback |
| uint8_t trigger = MmioRead8(regs + GPIOIS); |
| if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_LEVEL) { |
| trigger |= bit; |
| } else { |
| trigger &= ~bit; |
| } |
| MmioRead8(trigger, regs + GPIOIS); |
| |
| uint8_t be = MmioRead8(regs + GPIOIBE); |
| uint8_t iev = MmioRead8(regs + GPIOIEV); |
| |
| if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_EDGE && (flags & GPIO_TRIGGER_RISING) |
| && (flags & GPIO_TRIGGER_FALLING)) { |
| be |= bit; |
| } else { |
| be &= ~bit; |
| } |
| if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_EDGE && (flags & GPIO_TRIGGER_RISING) |
| && !(flags & GPIO_TRIGGER_FALLING)) { |
| iev |= bit; |
| } else { |
| iev &= ~bit; |
| } |
| |
| MmioWrite8(be, regs + GPIOIBE); |
| MmioWrite8(iev, regs + GPIOIEV); |
| */ |
| |
| // TODO(voydanoff) Implement GPIO_PULL_* flags |
| |
| mtx_unlock(&gpios->lock); |
| return ZX_OK; |
| } |
| |
| static zx_status_t pl061_gpio_config_out(void* ctx, uint32_t index, uint8_t initial_value) { |
| pl061_gpios_t* gpios = ctx; |
| index -= gpios->gpio_start; |
| MMIO_PTR volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE); |
| uint8_t bit = 1 << (index % GPIOS_PER_PAGE); |
| |
| mtx_lock(&gpios->lock); |
| // write value first |
| MmioWrite8((initial_value ? bit : 0), regs + GPIODATA(bit)); |
| |
| // then set direction to OUT |
| uint8_t dir = MmioRead8(regs + GPIODIR); |
| dir |= bit; |
| MmioWrite8(dir, regs + GPIODIR); |
| |
| mtx_unlock(&gpios->lock); |
| return ZX_OK; |
| } |
| |
| static zx_status_t pl061_gpio_set_alt_function(void* ctx, uint32_t index, uint64_t function) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| static zx_status_t pl061_gpio_read(void* ctx, uint32_t index, uint8_t* out_value) { |
| pl061_gpios_t* gpios = ctx; |
| index -= gpios->gpio_start; |
| MMIO_PTR volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE); |
| uint8_t bit = 1 << (index % GPIOS_PER_PAGE); |
| |
| *out_value = !!(MmioRead8(regs + GPIODATA(bit)) & bit); |
| return ZX_OK; |
| } |
| |
| static zx_status_t pl061_gpio_write(void* ctx, uint32_t index, uint8_t value) { |
| pl061_gpios_t* gpios = ctx; |
| index -= gpios->gpio_start; |
| MMIO_PTR volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE); |
| uint8_t bit = 1 << (index % GPIOS_PER_PAGE); |
| |
| MmioWrite8((value ? bit : 0), regs + GPIODATA(bit)); |
| return ZX_OK; |
| } |
| |
| static zx_status_t pl061_gpio_get_interrupt(void* ctx, uint32_t pin, uint32_t flags, |
| zx_handle_t* out_handle) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| static zx_status_t pl061_gpio_release_interrupt(void* ctx, uint32_t pin) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| static zx_status_t pl061_gpio_set_polarity(void* ctx, uint32_t pin, uint32_t polarity) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| 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, |
| .read = pl061_gpio_read, |
| .write = pl061_gpio_write, |
| .get_interrupt = pl061_gpio_get_interrupt, |
| .release_interrupt = pl061_gpio_release_interrupt, |
| .set_polarity = pl061_gpio_set_polarity, |
| }; |