1// Copyright 2017 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 <ddk/debug.h>
6#include <gpio/pl061/pl061.h>
7#include <soc/hi3660/hi3660.h>
8
9#include <assert.h>
10#include <stdlib.h>
11#include <stdio.h>
12
13// TODO(voydanoff) Move hard coded values to a header file
14
15// Addresses for GPIO regions
16#define GPIO_0_ADDR     0xe8a0b000
17#define GPIO_18_ADDR    0xff3b4000
18#define GPIO_20_ADDR    0xe8a1f000
19#define GPIO_22_ADDR    0xfff0b000
20#define GPIO_28_ADDR    0xfff1d000
21
22static pl061_gpios_t* find_gpio(hi3660_t* hi3660, uint32_t index) {
23    pl061_gpios_t* gpios;
24    // TODO(voydanoff) consider using a fancier data structure here
25    list_for_every_entry(&hi3660->gpios, gpios, pl061_gpios_t, node) {
26        if (index >= gpios->gpio_start && index < gpios->gpio_start + gpios->gpio_count) {
27            return gpios;
28        }
29    }
30    zxlogf(ERROR, "find_gpio failed for index %u\n", index);
31    return NULL;
32}
33
34static zx_status_t hi3660_gpio_config_in(void* ctx, uint32_t index, uint32_t flags) {
35    hi3660_t* hi3660 = ctx;
36    pl061_gpios_t* gpios = find_gpio(hi3660, index);
37    if (!gpios) {
38        return ZX_ERR_INVALID_ARGS;
39    }
40    return pl061_proto_ops.config_in(gpios, index, flags);
41}
42
43static zx_status_t hi3660_gpio_config_out(void* ctx, uint32_t index, uint8_t initial_value) {
44    hi3660_t* hi3660 = ctx;
45    pl061_gpios_t* gpios = find_gpio(hi3660, index);
46    if (!gpios) {
47        return ZX_ERR_INVALID_ARGS;
48    }
49    return pl061_proto_ops.config_out(gpios, index, initial_value);
50}
51
52static zx_status_t hi3660_gpio_set_alt_function(void* ctx, uint32_t index, uint64_t function) {
53    return ZX_ERR_NOT_SUPPORTED;
54}
55
56static zx_status_t hi3660_gpio_read(void* ctx, uint32_t index, uint8_t* out_value) {
57    hi3660_t* hi3660 = ctx;
58    pl061_gpios_t* gpios = find_gpio(hi3660, index);
59    if (!gpios) {
60        return ZX_ERR_INVALID_ARGS;
61    }
62    return pl061_proto_ops.read(gpios, index, out_value);
63}
64
65static zx_status_t hi3660_gpio_write(void* ctx, uint32_t index, uint8_t value) {
66    hi3660_t* hi3660 = ctx;
67    pl061_gpios_t* gpios = find_gpio(hi3660, index);
68    if (!gpios) {
69        return ZX_ERR_INVALID_ARGS;
70    }
71    return pl061_proto_ops.write(gpios, index, value);
72}
73
74static zx_status_t hi3660_gpio_get_interrupt(void* ctx, uint32_t pin, uint32_t flags,
75                                             zx_handle_t* out_handle) {
76    return ZX_ERR_NOT_SUPPORTED;
77}
78
79static zx_status_t hi3660_gpio_release_interrupt(void* ctx, uint32_t pin) {
80    return ZX_ERR_NOT_SUPPORTED;
81}
82
83static zx_status_t hi3660_gpio_set_polarity(void* ctx, uint32_t pin, uint32_t polarity) {
84    return ZX_ERR_NOT_SUPPORTED;
85}
86
87static gpio_impl_protocol_ops_t gpio_ops = {
88    .config_in = hi3660_gpio_config_in,
89    .config_out = hi3660_gpio_config_out,
90    .set_alt_function = hi3660_gpio_set_alt_function,
91    .read = hi3660_gpio_read,
92    .write = hi3660_gpio_write,
93    .get_interrupt = hi3660_gpio_get_interrupt,
94    .release_interrupt = hi3660_gpio_release_interrupt,
95    .set_polarity = hi3660_gpio_set_polarity,
96};
97
98typedef struct {
99    zx_paddr_t  base;
100    size_t      length;
101    uint32_t    start_pin;
102    uint32_t    pin_count;
103    const uint32_t* irqs;
104    uint32_t    irq_count;
105} gpio_block_t;
106
107static const uint32_t irqs_0[] = {
108    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
109};
110
111static const uint32_t irqs_18[] = {
112    134, 135,
113};
114
115static const uint32_t irqs_20[] = {
116    136, 137,
117};
118
119static const uint32_t irqs_22[] = {
120    138, 139, 140, 141, 142, 143,
121};
122
123static const uint32_t irqs_28[] = {
124    173,
125};
126
127static const gpio_block_t gpio_blocks[] = {
128    {
129        // GPIO groups 0 - 17
130        .base = GPIO_0_ADDR,
131        .length = 18 * 4096,
132        .start_pin = 0,
133        .pin_count = 18 * 8,
134        .irqs = irqs_0,
135        .irq_count = countof(irqs_0),
136    },
137    {
138        // GPIO groups 18 and 19
139        .base = GPIO_18_ADDR,
140        .length = 2 * 4096,
141        .start_pin = 18 * 8,
142        .pin_count = 2 * 8,
143        .irqs = irqs_18,
144        .irq_count = countof(irqs_18),
145    },
146    {
147        // GPIO groups 20 and 21
148        .base = GPIO_20_ADDR,
149        .length = 2 * 4096,
150        .start_pin = 20 * 8,
151        .pin_count = 2 * 8,
152        .irqs = irqs_20,
153        .irq_count = countof(irqs_20),
154    },
155    {
156        // GPIO groups 22 - 27
157        .base = GPIO_22_ADDR,
158        .length = 6 * 4096,
159        .start_pin = 22 * 8,
160        .pin_count = 6 * 8,
161        .irqs = irqs_22,
162        .irq_count = countof(irqs_22),
163    },
164    {
165        // GPIO group 28
166        .base = GPIO_28_ADDR,
167        .length = 1 * 4096,
168        .start_pin = 28 * 8,
169        .pin_count = 1 * 8,
170        .irqs = irqs_28,
171        .irq_count = countof(irqs_28),
172    },
173};
174
175zx_status_t hi3660_gpio_init(hi3660_t* hi3660, zx_handle_t bti) {
176    zx_status_t status;
177    zx_handle_t resource = get_root_resource();
178
179    for (size_t i = 0; i < countof(gpio_blocks); i++) {
180        const gpio_block_t* block = &gpio_blocks[i];
181
182        pl061_gpios_t* gpios = calloc(1, sizeof(pl061_gpios_t));
183        if (!gpios) {
184            return ZX_ERR_NO_MEMORY;
185        }
186
187        status = io_buffer_init_physical(&gpios->buffer, bti, block->base, block->length,
188                                         resource, ZX_CACHE_POLICY_UNCACHED_DEVICE);
189        if (status != ZX_OK) {
190            zxlogf(ERROR, "hi3660_gpio_init: io_buffer_init_physical failed %d\n", status);
191            free(gpios);
192            return status;
193        }
194
195        mtx_init(&gpios->lock, mtx_plain);
196        gpios->gpio_start = block->start_pin;
197        gpios->gpio_count = block->pin_count;
198        gpios->irqs = block->irqs;
199        gpios->irq_count = block->irq_count;
200        list_add_tail(&hi3660->gpios, &gpios->node);
201    }
202
203    hi3660->gpio.ops = &gpio_ops;
204    hi3660->gpio.ctx = hi3660;
205
206    return ZX_OK;
207}
208
209void hi3660_gpio_release(hi3660_t* hi3660) {
210    pl061_gpios_t* gpios;
211
212    while ((gpios = list_remove_head_type(&hi3660->gpios, pl061_gpios_t, node)) != NULL) {
213        io_buffer_release(&gpios->buffer);
214        free(gpios);
215    }
216}
217