blob: 47899f4faf39dde6c48ac1ca0c5660200a7fd413 [file] [log] [blame]
// 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 <ddk/debug.h>
#include <gpio/pl061/pl061.h>
#include <soc/hi3660/hi3660.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
// TODO(voydanoff) Move hard coded values to a header file
// Addresses for GPIO regions
#define GPIO_0_ADDR 0xe8a0b000
#define GPIO_18_ADDR 0xff3b4000
#define GPIO_20_ADDR 0xe8a1f000
#define GPIO_22_ADDR 0xfff0b000
#define GPIO_28_ADDR 0xfff1d000
static pl061_gpios_t* find_gpio(hi3660_t* hi3660, uint32_t index) {
pl061_gpios_t* gpios;
// TODO(voydanoff) consider using a fancier data structure here
list_for_every_entry(&hi3660->gpios, gpios, pl061_gpios_t, node) {
if (index >= gpios->gpio_start && index < gpios->gpio_start + gpios->gpio_count) {
return gpios;
}
}
zxlogf(ERROR, "find_gpio failed for index %u\n", index);
return NULL;
}
static zx_status_t hi3660_gpio_config_in(void* ctx, uint32_t index, uint32_t flags) {
hi3660_t* hi3660 = ctx;
pl061_gpios_t* gpios = find_gpio(hi3660, index);
if (!gpios) {
return ZX_ERR_INVALID_ARGS;
}
return pl061_proto_ops.config_in(gpios, index, flags);
}
static zx_status_t hi3660_gpio_config_out(void* ctx, uint32_t index, uint8_t initial_value) {
hi3660_t* hi3660 = ctx;
pl061_gpios_t* gpios = find_gpio(hi3660, index);
if (!gpios) {
return ZX_ERR_INVALID_ARGS;
}
return pl061_proto_ops.config_out(gpios, index, initial_value);
}
static zx_status_t hi3660_gpio_set_alt_function(void* ctx, uint32_t index, uint64_t function) {
return ZX_ERR_NOT_SUPPORTED;
}
static zx_status_t hi3660_gpio_read(void* ctx, uint32_t index, uint8_t* out_value) {
hi3660_t* hi3660 = ctx;
pl061_gpios_t* gpios = find_gpio(hi3660, index);
if (!gpios) {
return ZX_ERR_INVALID_ARGS;
}
return pl061_proto_ops.read(gpios, index, out_value);
}
static zx_status_t hi3660_gpio_write(void* ctx, uint32_t index, uint8_t value) {
hi3660_t* hi3660 = ctx;
pl061_gpios_t* gpios = find_gpio(hi3660, index);
if (!gpios) {
return ZX_ERR_INVALID_ARGS;
}
return pl061_proto_ops.write(gpios, index, value);
}
static zx_status_t hi3660_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 hi3660_gpio_release_interrupt(void* ctx, uint32_t pin) {
return ZX_ERR_NOT_SUPPORTED;
}
static zx_status_t hi3660_gpio_set_polarity(void* ctx, uint32_t pin, uint32_t polarity) {
return ZX_ERR_NOT_SUPPORTED;
}
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,
.read = hi3660_gpio_read,
.write = hi3660_gpio_write,
.get_interrupt = hi3660_gpio_get_interrupt,
.release_interrupt = hi3660_gpio_release_interrupt,
.set_polarity = hi3660_gpio_set_polarity,
};
typedef struct {
zx_paddr_t base;
size_t length;
uint32_t start_pin;
uint32_t pin_count;
const uint32_t* irqs;
uint32_t irq_count;
} gpio_block_t;
static const uint32_t irqs_0[] = {
116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
};
static const uint32_t irqs_18[] = {
134, 135,
};
static const uint32_t irqs_20[] = {
136, 137,
};
static const uint32_t irqs_22[] = {
138, 139, 140, 141, 142, 143,
};
static const uint32_t irqs_28[] = {
173,
};
static const gpio_block_t gpio_blocks[] = {
{
// GPIO groups 0 - 17
.base = GPIO_0_ADDR,
.length = 18 * 4096,
.start_pin = 0,
.pin_count = 18 * 8,
.irqs = irqs_0,
.irq_count = countof(irqs_0),
},
{
// GPIO groups 18 and 19
.base = GPIO_18_ADDR,
.length = 2 * 4096,
.start_pin = 18 * 8,
.pin_count = 2 * 8,
.irqs = irqs_18,
.irq_count = countof(irqs_18),
},
{
// GPIO groups 20 and 21
.base = GPIO_20_ADDR,
.length = 2 * 4096,
.start_pin = 20 * 8,
.pin_count = 2 * 8,
.irqs = irqs_20,
.irq_count = countof(irqs_20),
},
{
// GPIO groups 22 - 27
.base = GPIO_22_ADDR,
.length = 6 * 4096,
.start_pin = 22 * 8,
.pin_count = 6 * 8,
.irqs = irqs_22,
.irq_count = countof(irqs_22),
},
{
// GPIO group 28
.base = GPIO_28_ADDR,
.length = 1 * 4096,
.start_pin = 28 * 8,
.pin_count = 1 * 8,
.irqs = irqs_28,
.irq_count = countof(irqs_28),
},
};
zx_status_t hi3660_gpio_init(hi3660_t* hi3660) {
zx_status_t status;
zx_handle_t resource = get_root_resource();
for (size_t i = 0; i < countof(gpio_blocks); i++) {
const gpio_block_t* block = &gpio_blocks[i];
pl061_gpios_t* gpios = calloc(1, sizeof(pl061_gpios_t));
if (!gpios) {
return ZX_ERR_NO_MEMORY;
}
status = mmio_buffer_init_physical(&gpios->buffer, block->base, block->length,
resource, ZX_CACHE_POLICY_UNCACHED_DEVICE);
if (status != ZX_OK) {
zxlogf(ERROR, "hi3660_gpio_init: mmio_buffer_init_physical failed %d\n", status);
free(gpios);
return status;
}
mtx_init(&gpios->lock, mtx_plain);
gpios->gpio_start = block->start_pin;
gpios->gpio_count = block->pin_count;
gpios->irqs = block->irqs;
gpios->irq_count = block->irq_count;
list_add_tail(&hi3660->gpios, &gpios->node);
}
hi3660->gpio.ops = &gpio_ops;
hi3660->gpio.ctx = hi3660;
return ZX_OK;
}
void hi3660_gpio_release(hi3660_t* hi3660) {
pl061_gpios_t* gpios;
while ((gpios = list_remove_head_type(&hi3660->gpios, pl061_gpios_t, node)) != NULL) {
mmio_buffer_release(&gpios->buffer);
free(gpios);
}
}